diff --git a/CMakeLists.txt b/CMakeLists.txt index 73156513748024b6976eb69b627c11188e070b74..ae4a5229fa0fd0eb56a2bf68baa591cfecf2947f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,16 @@ if(SCP_FIRMWARE_SOURCE_DIR) include("${SCP_FIRMWARE_SOURCE_DIR}/Firmware.cmake") + # create a combined DT_MODULE list. + list(APPEND SCP_DT_CONFIG_MODULES_ALL ${SCP_DT_CONFIG_MODULES_STD}) + list(APPEND SCP_DT_CONFIG_MODULES_ALL ${SCP_DT_CONFIG_MODULES_DYN}) + + # temp.. + message(STATUS "modules = ${SCP_MODULES}") + message(STATUS "dt_std_modules = ${SCP_DT_CONFIG_MODULES_STD}") + message(STATUS "dt_dyn_modules = ${SCP_DT_CONFIG_MODULES_DYN}") + message(STATUS "dt_all_modules = ${SCP_DT_CONFIG_MODULES_ALL}") + if((NOT SCP_FIRMWARE) OR (NOT SCP_FIRMWARE_TARGET)) # cmake-format: off message(FATAL_ERROR @@ -159,6 +169,14 @@ cmake_dependent_option( set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) +# +# Stop after C Pre-Processor - allow debug of macros. +# +if (SCP_BUILD_CPP_ONLY) + string(APPEND CMAKE_C_FLAGS " -E") +endif() + + # # Set up Cppcheck if it's available and enabled. # @@ -481,14 +499,22 @@ if(SCP_FIRMWARE) # support other modules. # - add_subdirectory("module" EXCLUDE_FROM_ALL) - + add_subdirectory("module" EXCLUDE_FROM_ALL) + # # Load in the framework. This should be the last of the targets we need. # add_subdirectory("framework" EXCLUDE_FROM_ALL) + # + # generate any code needed from .dts files + # + if(SCP_FIRMWARE_DTS_SOURCE) + message(STATUS "Using .dts ${SCP_FIRMWARE_DTS_SOURCE}") + include(dts) + endif() + # # Collect up all the source files used to build the various targets created # above. diff --git a/Makefile.cmake b/Makefile.cmake index 6138ee4c493e51a036acd5e9c1b6fdceb9158620..f4296616dba4817f65bde8a9aa7e54f607945783 100644 --- a/Makefile.cmake +++ b/Makefile.cmake @@ -119,9 +119,24 @@ ifneq ($(filter-out $(PRODUCT_INDEPENDENT_GOALS), $(MAKECMDGOALS)),) $(error "You must define BS_FIRMWARE_LIST in product.mk. Aborting...") endif - FIRMWARE_TARGETS := $(addprefix firmware-, $(BS_FIRMWARE_LIST)) + ifeq ($(BUILD_TARGET),) + FIRMWARE_TARGETS := $(addprefix firmware-, $(BS_FIRMWARE_LIST)) + else + ifneq ($(findstring $(BUILD_TARGET),$(BS_FIRMWARE_LIST)),) + FIRMWARE_TARGETS := $(addprefix firmware-, $(BUILD_TARGET)) + else + $(error "Specific BUILD_TARGET $(BUILD_TARGET) not in valid target list: $(BS_FIRMWARE_LIST)") + endif + endif + + PRODUCT_BUILD_PATH := $(BUILD_PATH)/$(BS_PRODUCT_NAME)/$(TOOLCHAIN)/$(MODE) -PRODUCT_BUILD_PATH := $(BUILD_PATH)/$(BS_PRODUCT_NAME)/$(TOOLCHAIN)/$(MODE) + GENERATE_PREFIXES := $(addprefix $(PRODUCT_BUILD_PATH)/, $(FIRMWARE_TARGETS)) + GENERATE_TARGETS := $(addsuffix /CMakeCache.txt, $(GENERATE_PREFIXES)) + + ifneq ($(CPP_HALT),) + CPP_HALT_ARGS := -DSCP_BUILD_CPP_ONLY=1 + endif define msg_start ================================================================ @@ -129,10 +144,12 @@ Arm SCP/MCP Software build System Platform : $(BS_PRODUCT_NAME) Mode : $(MODE) Firmware(s) : $(BS_FIRMWARE_LIST) +Targets(s) : $(FIRMWARE_TARGETS) ================================================================ endef $(info $(msg_start)) + endif ifeq ($(MODE),debug) @@ -154,6 +171,7 @@ endif CMAKE_COMMAND_OPTION += $(CMAKE_TOOL_LOG_LEVEL) CMAKE_COMMAND_OPTION += $(CMAKE_SCP_LOG_LEVEL_OPTION) CMAKE_COMMAND_OPTION += $(CMAKE_DEBUGGER_OPTION) +CMAKE_COMMAND_OPTION += $(CPP_HALT_ARGS) # # Rules @@ -169,6 +187,8 @@ help: @echo "| Available Targets |" @echo "--------------------------------------------------------------------" @echo " all Build all firmware defined by PRODUCT=" + @echo " generate Generate all CMake build trees defined by " + @echo " PRODUCT=." @echo " clean Remove all built products" @echo " help Show this documentation" @echo "" @@ -218,6 +238,12 @@ help: @echo " Default: $(DEFAULT_BUILD_SYSTEM)" @echo " Specify CMake to generate GNU Make or Ninja build system." @echo "" + @echo " BUILD_TARGET" + @echo " Value: " + @echo " Default: " + @echo " Specify a named firmware target for the build - otherwise will" + @echo " build all valid firmware targets for the selected PRODUCT" + @echo "" @echo " TOOLCHAIN" @echo " Value: " @echo " Default: $(DEFAULT_TOOLCHAIN)" @@ -231,20 +257,44 @@ help: @echo " Value: " @echo " Default: " @echo " Pass extra arguments directly to cmake configuration stage." - @echo " Multiplle extra args can be added with += or by passing the arguments as a string" + @echo " Multiple extra args can be added with += or by passing the arguments as a string" @echo "" @echo " EXTRA_BUILD_ARGS" @echo " Value: " @echo " Default: " @echo " Pass extra arguments directly to cmake build stage." - @echo " Multiplle extra args can be added with += or by passing the arguments as a string" + @echo " Multiple extra args can be added with += or by passing the arguments as a string" + @echo "" + @echo " CPP_HALT" + @echo " Value: 1 " + @echo " Default: " + @echo " Halt after the C Pre-Processor stage - use to debug macro expansion" @echo "" - .SECONDEXPANSION: .PHONY: all -all: $(FIRMWARE_TARGETS) +all: command_msg $(FIRMWARE_TARGETS) + +define msg_generate +================================================================ +Generate only: $(GENERATE_TARGETS) +================================================================ +endef + +.PHONY: generate +generate: generate_msg $(GENERATE_TARGETS) + +generate_msg: + $(info $(msg_generate)) + + +define msg_cmd +CMake Commnd options: $(CMAKE_COMMAND_OPTION) +endef + +command_msg: + $(info $(msg_cmd)) firmware-%: $(PRODUCT_BUILD_PATH)/$$@/CMakeCache.txt $(CMAKE) --build $( options + ${DTS_INC_FLAG_CPP} # dts source as -include options + -P + -E # stop after pre-processing + -MD # generate deps file + -MF ${DTS_CPP_DEPS_OUT} + -o ${DTS_CPP_TEMP_OUT} + ${CMAKE_SOURCE_DIR}/dts/misc/empty_file.c + WORKING_DIRECTORY ${DTS_BIN_DIR} + RESULT_VARIABLE ret + ) + if(NOT "${ret}" STREQUAL "0") + message(FATAL_ERROR "C pre-process command failed with return code: ${ret}") + endif() + + # run generator on preprocessed temp file. + set(CMD_GEN_CFG ${PYTHON_EXEC} ${DTS_GEN_SCRIPT} + --dts ${DTS_CPP_TEMP_OUT} + --scp-base-dir ${CMAKE_SOURCE_DIR} + --header-out ${DTS_FWK_HEADER_OUT} + --dts-out ${DTS_GEN_DTS_DBG_OUT} # As a debugging aid + --bindings-dirs ${DTS_BINDINGS_DIRS} + --edt-pickle-out ${DTS_EDT_PICKLE_OUT} + --vendor-prefixes ${DTS_VENDOR_PREFIXES} + --dtc-flags "none" + --verbose 1 + ) + message(STATUS "command used: ${CMD_GEN_CFG} raw3 in ${DTS_BIN_DIR}") + execute_process( + COMMAND ${CMD_GEN_CFG} + WORKING_DIRECTORY ${DTS_BIN_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) + + # add dependencies from processed .dts file + # Parse the generated dependency file to find the DT sources that + # were included and then add them to the list of files that trigger + # a re-run of CMake. + toolchain_parse_make_rule( + ${DTS_CPP_DEPS_OUT} + include_files # Output parameter + ) + + set_property(DIRECTORY APPEND PROPERTY + CMAKE_CONFIGURE_DEPENDS + ${include_files} + ${DTS_GEN_SCRIPT} + ) + +endif() diff --git a/doc/Doxyfile b/doc/Doxyfile index 4b3bf550bab4ab66ea802dba2eee79a38c960011..df6ce36540e3996b0dae3d1df5881a57b294b2b1 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -800,6 +800,7 @@ INPUT = $(TOP_DIR)/readme.md \ $(DOC_DIR)/framework.md \ $(DOC_DIR)/cmsis.md \ $(DOC_DIR)/glossary.md \ + $(DOC_DIR)/cmake_readme.md \ $(DOC_DIR)/deferred_response_architecture.md \ $(DOC_DIR)/architecture_support.md \ $(FWK_DIR)/include \ diff --git a/dts/bindings/arm,pl011.yaml b/dts/bindings/arm,pl011.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3ce0e9464f3747846ffac88ae5d0050312787c57 --- /dev/null +++ b/dts/bindings/arm,pl011.yaml @@ -0,0 +1,19 @@ +description: ARM PL011 UART + +compatible: "arm,pl011" + +include: uart-controller.yaml + +properties: + reg: + required: true + + # MJL: changed to false + interrupts: + required: false + + pd-id: + type: int + required: false + description: Power domain index for the component + diff --git a/dts/bindings/base.yaml b/dts/bindings/base.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1e489401e83612df40a0ff53bcf1626b628c6b56 --- /dev/null +++ b/dts/bindings/base.yaml @@ -0,0 +1,99 @@ +# Common fields for all devices + +include: [pm.yaml] + +properties: + status: + type: string + required: false + description: indicates the operational status of a device + enum: + - "ok" # Deprecated form + - "okay" + - "disabled" + - "reserved" + - "fail" + - "fail-sss" + + compatible: + type: string-array + required: true + description: compatible strings + + reg: + type: array + description: register space + required: false + + reg-names: + type: string-array + description: name of each register space + required: false + + interrupts: + type: array + required: false + description: interrupts for device + + # Does not follow the 'type: phandle-array' scheme, but gets type-checked + # by the code. Declare it here just so that other bindings can make it + # 'required: true' easily if they want to. + interrupts-extended: + type: compound + required: false + description: extended interrupt specifier for device + + interrupt-names: + type: string-array + required: false + description: name of each interrupt + + interrupt-parent: + type: phandle + required: false + description: phandle to interrupt controller node + + label: + type: string + required: false + description: Human readable string describing the device (used as device_get_binding() argument) + + clocks: + type: phandle-array + required: false + description: Clock gate information + + clock-names: + type: string-array + required: false + description: name of each clock + + "#address-cells": + type: int + required: false + description: number of address cells in reg property + + "#size-cells": + type: int + required: false + description: number of size cells in reg property + + dmas: + required: false + type: phandle-array + description: DMA channels specifiers + + dma-names: + required: false + type: string-array + description: Provided names of DMA channel specifiers + + io-channels: + required: false + type: phandle-array + description: IO channels specifiers + + io-channel-names: + required: false + type: string-array + description: Provided names of IO channel specifiers diff --git a/dts/bindings/pm.yaml b/dts/bindings/pm.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a0d3bae20335c9d6c6d75b7f2e2d0a1d2fbd6012 --- /dev/null +++ b/dts/bindings/pm.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Intel Corporation. +# SPDX-License-Identifier: Apache-2.0 + +# Properties for Power Management (PM) + +properties: + wakeup-source: + required: false + type: boolean + description: | + Property to identify that a device can be used as wake up source. + + When this property is provided a specific flag is set into the + device that tells the system that the device is capable of + wake up the system. + + Wake up capable devices are disabled (interruptions will not wake up + the system) by default but they can be enabled at runtime if necessary. diff --git a/dts/bindings/uart-controller.yaml b/dts/bindings/uart-controller.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d2674e14d979ecf5341f5e0648185d7f0c31cf56 --- /dev/null +++ b/dts/bindings/uart-controller.yaml @@ -0,0 +1,31 @@ +# Common fields for UART controllers + +include: base.yaml + +bus: uart + +properties: + clock-frequency: + type: int + required: false + description: Clock frequency information for UART operation + current-speed: + type: int + required: false + description: Initial baud rate setting for UART + label: + required: true + hw-flow-control: + type: boolean + required: false + description: Set to enable RTS/CTS flow control at boot time + parity: + required: false + type: string + description: | + Configures the parity of the adapter. Enumeration id 0 for none, 1 for odd + and 2 for even parity. Default to none if not specified. + enum: + - "none" + - "odd" + - "even" diff --git a/dts/bindings/vendor-prefixes.txt b/dts/bindings/vendor-prefixes.txt new file mode 100644 index 0000000000000000000000000000000000000000..f59a750f82b518d305c6b513cce1dec62450c489 --- /dev/null +++ b/dts/bindings/vendor-prefixes.txt @@ -0,0 +1,669 @@ +# Device tree binding vendor prefix registry. Keep this list in +# alphabetical order. +# +# This isn't an exhaustive list, but you should add new prefixes to it +# before using them to avoid name-space collisions. +# +# The contents of this file are parsed during documentation generation. +# Anything that starts with a '#' is treated as a comment and ignored. +# Non-empty lines should be in this format: +# +# + +abb ABB +abilis Abilis Systems +abracon Abracon Corporation +abt ShenZhen Asia Better Technology Ltd. +acer Acer Inc. +acme Acme Systems srl +actions Actions Semiconductor Co., Ltd. +active-semi Active-Semi International Inc +ad Avionic Design GmbH +adafruit Adafruit Industries, LLC +adapteva Adapteva, Inc. +adaptrum Adaptrum, Inc. +adh AD Holdings Plc. +adi Analog Devices, Inc. +advantech Advantech Corporation +aeroflexgaisler Aeroflex Gaisler AB +aesop AESOP Embedded Forum +al Annapurna Labs +alcatel Alcatel +allegro Allegro DVT +allo Allo.com +allwinner Allwinner Technology Co., Ltd. +alphascale AlphaScale Integrated Circuits Systems, Inc. +alps Alps Electric Co., Ltd. +alt Altus-Escon-Company BV +altr Altera Corp. +amarula Amarula Solutions +amazon Amazon.com, Inc. +amcc Applied Micro Circuits Corporation (APM, formally AMCC) +amd Advanced Micro Devices (AMD), Inc. +amediatech Shenzhen Amediatech Technology Co., Ltd +amlogic Amlogic, Inc. +ampere Ampere Computing LLC +ampire Ampire Co., Ltd. +ams AMS AG +amstaos AMS-Taos Inc. +analogix Analogix Semiconductor, Inc. +andestech Andes Technology Corporation +anvo Anvo-Systems Dresden GmbH +apm Applied Micro Circuits Corporation (APM) +apple Apple Inc. +aptina Aptina Imaging +arasan Arasan Chip Systems +archermind ArcherMind Technology (Nanjing) Co., Ltd. +arctic Arctic Sand +arcx arcx Inc. / Archronix Inc. +arduino Arduino +aries Aries Embedded GmbH +arm ARM Ltd. +armadeus ARMadeus Systems SARL +arrow Arrow Electronics +artesyn Artesyn Embedded Technologies Inc. +asahi-kasei Asahi Kasei Corp. +asmedia ASMedia Technology Inc. +asc All Sensors Corporation +aspeed ASPEED Technology Inc. +asus AsusTek Computer Inc. +atlas Atlas Scientific LLC +atmel Atmel Corporation +auo AU Optronics Corporation +auvidea Auvidea GmbH +avago Avago Technologies +avia avia semiconductor +avic Shanghai AVIC Optoelectronics Co., Ltd. +avnet Avnet, Inc. +awinic Shanghai Awinic Technology Co., Ltd. +axentia Axentia Technologies AB +axis Axis Communications AB +azoteq Azoteq (Pty) Ltd +azw Shenzhen AZW Technology Co., Ltd. +baikal BAIKAL ELECTRONICS, JSC +bananapi BIPAI KEJI LIMITED +beacon Compass Electronics Group, LLC +beagle BeagleBoard.org Foundation +bhf Beckhoff Automation GmbH & Co. KG +bitmain Bitmain Technologies +blutek BluTek Power +boe BOE Technology Group Co., Ltd. +bosch Bosch Sensortec GmbH +boundary Boundary Devices Inc. +broadmobi Shanghai Broadmobi Communication Technology Co.,Ltd. +brcm Broadcom Corporation +buffalo Buffalo, Inc. +bur B&R Industrial Automation GmbH +bticino Bticino International +cadence Cadence Design Systems, Inc. +calaosystems CALAO Systems SAS +calxeda Calxeda +canaan Canaan, Inc. +caninos Caninos Loucos Program +capella Capella Microsystems, Inc +cascoda Cascoda, Ltd. +catalyst Catalyst Semiconductor, Inc. +cavium Cavium, Inc. +cdns Cadence Design Systems Inc. +cdtech CDTech(H.K.) Electronics Limited +cellwise CellWise Microelectronics Co., Ltd +ceva Ceva, Inc. +checkpoint Check Point Software Technologies Ltd. +chefree Chefree Technology Corp. +chipidea Chipidea, Inc +chipone ChipOne +chipspark ChipSPARK +chrontel Chrontel, Inc. +chrp Common Hardware Reference Platform +chunghwa Chunghwa Picture Tubes Ltd. +chuwi Chuwi Innovation Ltd. +ciaa Computadora Industrial Abierta Argentina +cirrus Cirrus Logic, Inc. +cisco Cisco Systems, Inc. +cloudengines Cloud Engines, Inc. +cnm Chips&Media, Inc. +cnxt Conexant Systems, Inc. +colorfly Colorful GRP, Shenzhen Xueyushi Technology Ltd. +compulab CompuLab Ltd. +coreriver CORERIVER Semiconductor Co.,Ltd. +corpro Chengdu Corpro Technology Co., Ltd. +cortina Cortina Systems, Inc. +cosmic Cosmic Circuits +crane Crane Connectivity Solutions +creative Creative Technology Ltd +crystalfontz Crystalfontz America, Inc. +csky Hangzhou C-SKY Microsystems Co., Ltd +csq Shenzen Chuangsiqi Technology Co.,Ltd. +cubietech Cubietech, Ltd. +cypress Cypress Semiconductor Corporation +cznic CZ.NIC, z.s.p.o. +dallas Maxim Integrated Products (formerly Dallas Semiconductor) +dataimage DataImage, Inc. +davicom DAVICOM Semiconductor, Inc. +dell Dell Inc. +delta Delta Electronics, Inc. +denx Denx Software Engineering +devantech Devantech, Ltd. +dfi DFI Inc. +dh DH electronics GmbH +difrnce Shenzhen Yagu Electronic Technology Co., Ltd. +digi Digi International Inc. +digilent Diglent, Inc. +dioo Dioo Microcircuit Co., Ltd +dlc DLC Display Co., Ltd. +dlg Dialog Semiconductor +dlink D-Link Corporation +dmo Data Modul AG +domintech Domintech Co., Ltd. +dongwoon Dongwoon Anatech +dptechnics DPTechnics +dragino Dragino Technology Co., Limited +dserve dServe Technology B.V. +dynaimage Dyna-Image +ea Embedded Artists AB +ebang Zhejiang Ebang Communication Co., Ltd +ebs-systart EBS-SYSTART GmbH +ebv EBV Elektronik +eckelmann Eckelmann AG +edt Emerging Display Technologies +eeti eGalax_eMPIA Technology Inc +einfochips Einfochips +elan Elan Microelectronic Corp. +element14 Element14 (A Premier Farnell Company) +elgin Elgin S/A. +elida Shenzhen Elida Technology Co., Ltd. +elimo Elimo Engineering Ltd. +embest Shenzhen Embest Technology Co., Ltd. +emlid Emlid, Ltd. +emmicro EM Microelectronic +empire-electronix Empire Electronix +emtrion emtrion GmbH +endless Endless Mobile, Inc. +ene ENE Technology, Inc. +energymicro Silicon Laboratories (formerly Energy Micro AS) +engicam Engicam S.r.l. +epcos EPCOS AG +epfl Ecole Polytechnique Fédérale de Lausanne +epson Seiko Epson Corp. +espressif Espressif Systems +est ESTeem Wireless Modems +ettus NI Ettus Research +eukrea Eukréa Electromatique +everest Everest Semiconductor Co. Ltd. +everspin Everspin Technologies, Inc. +evervision Evervision Electronics Co. Ltd. +exar Exar Corporation +excito Excito +ezchip EZchip Semiconductor +facebook Facebook +fairphone Fairphone B.V. +faraday Faraday Technology Corporation +fastrax Fastrax Oy +fcs Fairchild Semiconductor +feixin Shenzhen Feixin Photoelectic Co., Ltd +feiyang Shenzhen Fly Young Technology Co.,LTD. +fii Foxconn Industrial Internet +firefly Firefly +focaltech FocalTech Systems Co.,Ltd +frida Shenzhen Frida LCD Co., Ltd. +friendlyarm Guangzhou FriendlyARM Computer Tech Co., Ltd +fsl Freescale Semiconductor +fujitsu Fujitsu Ltd. +gaisler Gaisler +gardena GARDENA GmbH +gateworks Gateworks Corporation +gcw Game Consoles Worldwide +ge General Electric Company +geekbuying GeekBuying +gef GE Fanuc Intelligent Platforms Embedded Systems, Inc. +GEFanuc GE Fanuc Intelligent Platforms Embedded Systems, Inc. +gemei Gemei Digital Technology Co., Ltd. +geniatech Geniatech, Inc. +giantec Giantec Semiconductor, Inc. +giantplus Giantplus Technology Co., Ltd. +globalscale Globalscale Technologies, Inc. +globaltop GlobalTop Technology, Inc. +gmt Global Mixed-mode Technology, Inc. +goodix Shenzhen Huiding Technology Co., Ltd. +google Google, Inc. +grinn Grinn +grmn Garmin Limited +gumstix Gumstix, Inc. +hannstar HannStar Display Corporation +haoyu Haoyu Microelectronic Co. Ltd. +hardkernel Hardkernel Co., Ltd +hideep HiDeep Inc. +himax Himax Technologies, Inc. +hirschmann Hirschmann Automation and Control GmbH +hisilicon Hisilicon Limited. +hit Hitachi Ltd. +hitex Hitex Development Tools +holt Holt Integrated Circuits, Inc. +honestar Honestar Technologies Co., Ltd. +honeywell Honeywell +hoperun Jiangsu HopeRun Software Co., Ltd. +hp Hewlett Packard +hsg HannStar Display Co. +holtek Holtek Semiconductor, Inc. +hugsun Shenzhen Hugsun Technology Co. Ltd. +hwacom HwaCom Systems Inc. +hycon Hycon Technology Corp. +hydis Hydis Technologies +hyundai Hyundai Technology +i2se I2SE GmbH +ibm International Business Machines (IBM) +icplus IC Plus Corp. +idt Integrated Device Technologies, Inc. +ifi Ingenieurburo Fur Ic-Technologie (I/F/I) +ilitek ILI Technology Corporation (ILITEK) +img Imagination Technologies Ltd. +imi Integrated Micro-Electronics Inc. +incircuit In-Circuit GmbH +inet-tek Shenzhen iNet Mobile Internet Technology Co., Ltd +infineon Infineon Technologies +inforce Inforce Computing +ivo InfoVision Optoelectronics Kunshan Co. Ltd. +ingenic Ingenic Semiconductor +innolux Innolux Corporation +inside-secure INSIDE Secure +inspur Inspur Corporation +intel Intel Corporation +intercontrol Inter Control Group +invensense InvenSense Inc. +inversepath Inverse Path +iom Iomega Corporation +isee ISEE 2007 S.L. +isil Intersil +issi Integrated Silicon Solutions Inc. +ite ITE Tech. Inc. +itead ITEAD Intelligent Systems Co.Ltd +iwave iWave Systems Technologies Pvt. Ltd. +jdi Japan Display Inc. +jedec JEDEC Solid State Technology Association +jesurun Shenzhen Jesurun Electronics Business Dept. +jianda Jiandangjing Technology Co., Ltd. +kam Kamstrup A/S +karo Ka-Ro electronics GmbH +keithkoep Keith & Koep GmbH +keymile Keymile GmbH +khadas Khadas +kiebackpeter Kieback & Peter GmbH +kinetic Kinetic Technologies +kingdisplay King & Display Technology Co., Ltd. +kingnovel Kingnovel Technology Co., Ltd. +kionix Kionix, Inc. +kobo Rakuten Kobo Inc. +kobol Kobol Innovations Pte. Ltd. +koe Kaohsiung Opto-Electronics Inc. +kontron Kontron S&T AG +kosagi Sutajio Ko-Usagi PTE Ltd. +kvg Kverneland Group +kyo Kyocera Corporation +lacie LaCie +laird Laird PLC +lairdconnect Laird Connectivity +lamobo Ketai Huajie Technology Co., Ltd. +lantiq Lantiq Semiconductor +lattice Lattice Semiconductor +leadtek Shenzhen Leadtek Technology Co., Ltd. +leez Leez +lego LEGO Systems A/S +lemaker Shenzhen LeMaker Technology Co., Ltd. +lenovo Lenovo Group Ltd. +lg LG Corporation +lgphilips LG Display +libretech Shenzhen Libre Technology Co., Ltd +licheepi Lichee Pi +linaro Linaro Limited +linksprite LinkSprite Technologies, Inc. +linksys Belkin International, Inc. (Linksys) +linutronix Linutronix GmbH +linux Linux-specific binding +linx Linx Technologies +litex LiteX SoC builder +lltc Linear Technology Corporation +logicpd Logic PD, Inc. +logictechno Logic Technologies Limited +longcheer Longcheer Technology (Shanghai) Co., Ltd. +lontium Lontium Semiconductor Corporation +loongson Loongson Technology Corporation Limited +lsi LSI Corp. (LSI Logic) +lwn Liebherr-Werk Nenzing GmbH +lxa Linux Automation GmbH +m5stack M5Stack +macnica Macnica Americas +mantix Mantix Display Technology Co.,Ltd. +mapleboard Mapleboard.org +marvell Marvell Technology Group Ltd. +maxbotix MaxBotix Inc. +maxim Maxim Integrated Products +mbvl Mobiveil Inc. +mcube mCube +meas Measurement Specialties +mecer Mustek Limited +mediatek MediaTek Inc. +megachips MegaChips +mele Shenzhen MeLE Digital Technology Ltd. +melexis Melexis N.V. +melfas MELFAS Inc. +mellanox Mellanox Technologies +memsic MEMSIC Inc. +menlo Menlo Systems GmbH +mentor Mentor Graphics +meraki Cisco Meraki, LLC +merrii Merrii Technology Co., Ltd. +micrel Micrel Inc. +microchip Microchip Technology Inc. +microcrystal Micro Crystal AG +micron Micron Technology Inc. +microsoft Microsoft Corporation +microsys MicroSys Electronics GmbH +mikroe MikroElektronika d.o.o. +mikrotik MikroTik +miniand Miniand Tech +minix MINIX Technology Ltd. +miramems MiraMEMS Sensing Technology Co., Ltd. +mitsubishi Mitsubishi Electric Corporation +modtronix Modtronix Engineering +mosaixtech Mosaix Technologies, Inc. +motorola Motorola, Inc. +moxa Moxa Inc. +mpl MPL AG +mps Monolithic Power Systems Inc. +mqmaker mqmaker Inc. +mrvl Marvell Technology Group Ltd. +mscc Microsemi Corporation +msi Micro-Star International Co. Ltd. +mstar MStar Semiconductor, Inc. (acquired by MediaTek Inc.) +mti Imagination Technologies Ltd. (formerly MIPS Technologies Inc.) +multi-inno Multi-Inno Technology Co.,Ltd +mundoreader Mundo Reader S.L. +murata Murata Manufacturing Co., Ltd. +mxicy Macronix International Co., Ltd. +myir MYIR Tech Limited +national National Semiconductor +nec NEC LCD Technologies, Ltd. +neonode Neonode Inc. +netgear NETGEAR +netlogic Broadcom Corporation (formerly NetLogic Microsystems) +netron-dy Netron DY +netronix Netronix, Inc. +netxeon Shenzhen Netxeon Technology CO., LTD +neweast Guangdong Neweast Optoelectronics CO., LTD +nexbox Nexbox +nextthing Next Thing Co. +newhaven Newhaven Display International +ni National Instruments +nintendo Nintendo +nlt NLT Technologies, Ltd. +nokia Nokia +nordic Nordic Semiconductor +novtech NovTech, Inc. +nutsboard NutsBoard +nuvoton Nuvoton Technology Corporation +nvd New Vision Display +nvidia NVIDIA +nxp NXP Semiconductors +oceanic Oceanic Systems (UK) Ltd. +oct Octavo Systems LLC +okaya Okaya Electric America, Inc. +oki Oki Electric Industry Co., Ltd. +olimex OLIMEX Ltd. +olpc One Laptop Per Child +ovti OmniVision Technologies Co., Ltd. +onion Onion Corporation +onnn ON Semiconductor Corp. +ontat On Tat Industrial Company +opalkelly Opal Kelly Incorporated +opencores OpenCores.org +openisa open-isa.org +openrisc OpenRISC.io +option Option NV +oranth Shenzhen Oranth Technology Co., Ltd. +ORCL Oracle Corporation +orisetech Orise Technology +ortustech Ortus Technology Co., Ltd. +osddisplays OSD Displays +ouya Ouya Inc. +overkiz Overkiz SAS +ovti OmniVision Technologies +oxsemi Oxford Semiconductor, Ltd. +ozzmaker OzzMaker +panasonic Panasonic Corporation +parade Parade Technologies Inc. +particle Particle.io +parallax Parallax Inc. +pda Precision Design Associates, Inc. +pericom Pericom Technology Inc. +pervasive Pervasive Displays, Inc. +phicomm PHICOMM Co., Ltd. +phytec PHYTEC Messtechnik GmbH +picochip Picochip Ltd +pine64 Pine64 +pineriver Shenzhen PineRiver Designs Co., Ltd. +pixcir PIXCIR MICROELECTRONICS Co., Ltd +plantower Plantower Co., Ltd +plathome Plat'Home Co., Ltd. +plda PLDA +plx Broadcom Corporation (formerly PLX Technology) +ply Plymovent Group BV +pni PNI Sensor Corporation +pocketbook PocketBook International SA +polaroid Polaroid Corporation +portwell Portwell Inc. +poslab Poslab Technology Co., Ltd. +pov Point of View International B.V. +powertip Powertip Tech. Corp. +powervr PowerVR (deprecated, use img) +primux Primux Trading, S.L. +probox2 PROBOX2 (by W2COMP Co., Ltd.) +prt Protonic Holland +pulsedlight PulsedLight, Inc +purism Purism, SPC +qca Qualcomm Atheros, Inc. +qcom Qualcomm Technologies, Inc +qemu QEMU, a generic and open source machine emulator and virtualizer +qi Qi Hardware +qihua Chengdu Kaixuan Information Technology Co., Ltd. +qiaodian QiaoDian XianShi Corporation +qnap QNAP Systems, Inc. +quicklogic QuickLogic Corp. +radxa Radxa +raidsonic RaidSonic Technology GmbH +ralink Mediatek/Ralink Technology Corp. +ramtron Ramtron International +raspberrypi Raspberry Pi Foundation +raydium Raydium Semiconductor Corp. +rda Unisoc Communications, Inc. +realtek Realtek Semiconductor Corp. +remarkable reMarkable AS +renesas Renesas Electronics Corporation +rex iMX6 Rex Project +rervision Shenzhen Rervision Technology Co., Ltd. +revotics Revolution Robotics, Inc. (Revotics) +richtek Richtek Technology Corporation +ricoh Ricoh Co. Ltd. +rikomagic Rikomagic Tech Corp. Ltd +riscv RISC-V Foundation +riot Embest RIoT +rockchip Fuzhou Rockchip Electronics Co., Ltd +rocktech ROCKTECH DISPLAYS LIMITED +rohm ROHM Semiconductor Co., Ltd +ronbo Ronbo Electronics +roofull Shenzhen Roofull Technology Co, Ltd +ruuvi Ruuvi Innovations Ltd (Oy) +roseapplepi RoseapplePi.org +samsung Samsung Semiconductor +samtec Samtec/Softing company +sancloud Sancloud Ltd +sandisk Sandisk Corporation +satoz Satoz International Co., Ltd +sbs Smart Battery System +schindler Schindler +seagate Seagate Technology PLC +segger SEGGER Microcontroller GmbH +seeed Seeed Technology Co., Ltd +seirobotics Shenzhen SEI Robotics Co., Ltd +semtech Semtech Corporation +sensirion Sensirion AG +sensortek Sensortek Technology Corporation +sff Small Form Factor Committee +sgd Solomon Goldentek Display Corporation +sgmicro SG Micro Corp +sgx SGX Sensortech +sharp Sharp Corporation +shimafuji Shimafuji Electric, Inc. +shiratech Shiratech Solutions +si-en Si-En Technology Ltd. +si-linux Silicon Linux Corporation +sifive SiFive, Inc. +sigma Sigma Designs, Inc. +sii Seiko Instruments, Inc. +sil Silicon Image +silabs Silicon Laboratories +silead Silead Inc. +silergy Silergy Corp. +silex-insight Silex Insight +siliconfile Siliconfile Technologies lnc. +siliconmitus Silicon Mitus, Inc. +siemens Siemens AG +simtek Cypress Semiconductor Corporation (Simtek Corporation) +sinlinx Sinlinx Electronics Technology Co., LTD +sinovoip SinoVoip Co., Ltd +sipeed Shenzhen Sipeed Technology Co., Ltd. +sirf SiRF Technology, Inc. +sis Silicon Integrated Systems Corp. +sitronix Sitronix Technology Corporation +skyworks Skyworks Solutions, Inc. +smartlabs SmartLabs LLC +smsc Standard Microsystems Corporation +snps Synopsys, Inc. +sochip Shenzhen SoChip Technology Co., Ltd. +socionext Socionext Inc. +solidrun SolidRun +solomon Solomon Systech Limited +sony Sony Corporation +spansion Spansion Inc. +sprd Spreadtrum Communications Inc. +sst Silicon Storage Technology, Inc. +sstar Xiamen Xingchen(SigmaStar) Technology Co., Ltd. (formerly part of MStar Semiconductor, Inc.) +st STMicroelectronics +starry Starry Electronic Technology (ShenZhen) Co., LTD +startek Startek +ste ST-Ericsson +stericsson ST-Ericsson +st-ericsson ST-Ericsson +summit Summit microelectronics +sunchip Shenzhen Sunchip Technology Co., Ltd +SUNW Sun Microsystems, Inc +supermicro Super Micro Computer, Inc. +silvaco Silvaco, Inc. +swir Sierra Wireless +syna Synaptics Inc. +synology Synology, Inc. +tbs TBS Technologies +tbs-biometrics Touchless Biometric Systems AG +tcg Trusted Computing Group +tcl Toby Churchill Ltd. +tcs Shenzhen City Tang Cheng Technology Co., Ltd. +tdo Shangai Top Display Optoelectronics Co., Ltd +technexion TechNexion +technologic Technologic Systems +tempo Tempo Semiconductor +techstar Shenzhen Techstar Electronics Co., Ltd. +terasic Terasic Inc. +tfc Three Five Corp +thine THine Electronics, Inc. +thingyjp thingy.jp +ti Texas Instruments +tianma Tianma Micro-electronics Co., Ltd. +tlm Trusted Logic Mobility +tmt Tecon Microprocessor Technologies, LLC. +topeet Topeet +toppoly TPO (deprecated, use tpo) +topwise Topwise Communication Co., Ltd. +toradex Toradex AG +toshiba Toshiba Corporation +toumaz Toumaz +tpk TPK U.S.A. LLC +tplink TP-LINK Technologies Co., Ltd. +tpo TPO +tq TQ-Systems GmbH +tronfy Tronfy +tronsmart Tronsmart +truly Truly Semiconductors Limited +visionox Visionox +tsd Theobroma Systems Design und Consulting GmbH +tyan Tyan Computer Corporation +u-blox u-blox +u-boot U-Boot bootloader +ucrobotics uCRobotics +ubnt Ubiquiti Networks +udoo Udoo +ugoos Ugoos Industrial Co., Ltd. +uniwest United Western Technologies Corp (UniWest) +upisemi uPI Semiconductor Corp. +urt United Radiant Technology Corporation +usi Universal Scientific Industrial Co., Ltd. +utoo Aigo Digital Technology Co., Ltd. +v3 V3 Semiconductor +vaisala Vaisala +vamrs Vamrs Ltd. +variscite Variscite Ltd. +vdl Van der Laan b.v. +via VIA Technologies, Inc. +videostrong Videostrong Technology Co., Ltd. +virtio Virtual I/O Device Specification, developed by the OASIS consortium +virtual Used for virtual device without specific vendor. +vishay Vishay Intertechnology, Inc +vitesse Vitesse Semiconductor Corporation +vivante Vivante Corporation +vnd A stand-in for a real vendor which can be used in examples and tests +vocore VoCore Studio +voipac Voipac Technologies s.r.o. +vot Vision Optical Technology Co., Ltd. +vxt VXT Ltd +wand Wandbord (Technexion) +waveshare Waveshare Electronics +wd Western Digital Corp. +we Würth Elektronik GmbH. +weact WeAct Studio +wetek WeTek Electronics, limited. +wexler Wexler +whwave Shenzhen whwave Electronics, Inc. +wi2wi Wi2Wi, Inc. +winbond Winbond Electronics corp. +winstar Winstar Display Corp. +wits Shenzhen Merrii Technology Co., Ltd. (WITS) +wlf Wolfson Microelectronics +wm Wondermedia Technologies, Inc. +wobo Wobo +x-powers X-Powers +xes Extreme Engineering Solutions (X-ES) +xiaomi Xiaomi Technology Co., Ltd. +xillybus Xillybus Ltd. +xingbangda Shenzhen Xingbangda Display Technology Co., Ltd +xinpeng Shenzhen Xinpeng Technology Co., Ltd +xiphera Xiphera Ltd. +xlnx Xilinx +xnano Xnano +xunlong Shenzhen Xunlong Software CO.,Limited +xylon Xylon +yamaha Yamaha Corporation +yes-optoelectronics Yes Optoelectronics Co.,Ltd. +yic YIC System Co., Ltd. +ylm Shenzhen Yangliming Electronic Technology Co., Ltd. +yna YSH & ATIL +yones-toptech Yones Toptech Co., Ltd. +ys Shenzhen Yashi Changhua Intelligent Technology Co., Ltd. +ysoft Y Soft Corporation a.s. +zealz Zealz +zarlink Zarlink Semiconductor +zeitec ZEITEC Semiconductor Co., LTD. +zephyr Zephyr-specific binding +zidoo Shenzhen Zidoo Technology Co., Ltd. +zii Zodiac Inflight Innovations +zinitix Zinitix Co., Ltd +zkmagic Shenzhen Zkmagic Technology Co., Ltd. +zte ZTE Corp. +zyxel ZyXEL Communications Corp. diff --git a/dts/include/base.dtsi b/dts/include/base.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..b409a2fd0530f1b7700b34c9a82b4b0898d36f62 --- /dev/null +++ b/dts/include/base.dtsi @@ -0,0 +1,20 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* base level common device tree include */ + +/* include the framework common bindings */ +#include "fwk_dt_bindings.h" + + +/dts-v1/; +/ { + #address-cells = <1>; + #size-cells = <1>; + chosen { }; + aliases { }; +}; diff --git a/dts/misc/empty_file.c b/dts/misc/empty_file.c new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/dts/src/juno-scp-base.dtsi b/dts/src/juno-scp-base.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..8de9f166c764cbd566d6ee3dba75927e48e65219 --- /dev/null +++ b/dts/src/juno-scp-base.dtsi @@ -0,0 +1,51 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* base SCP .dtsi file for juno boards */ + +/* must be first include */ +#include "base.dtsi" + +#include "juno_dt_bindings.h" + + +/ { + soc { + #address-cells = <1>; + #size-cells = <1>; + + /* main console uart */ + uart1: uart@BC0A0000 { + compatible = "arm,pl011"; + reg = ; + current-speed = <115200>; + clock-frequency = <24000000>; + pd-id = ; +/* clocks = <&uartclk>;*/ +/* interrupts = <7 3>; */ +/* interrupt-names = "rx"; */ + label = "UART1"; + }; + + +/* another uart to validate generating arrays of elements */ + /* main console uart */ + uart0: uart@BC0B0000 { + compatible = "arm,pl011"; + reg = <0xBC0b0000 0x1000>; + current-speed = <115200>; + clock-frequency = <24000000>; + pd-id = ; +/* clocks = <&uartclk>;*/ +/* interrupts = <7 3>; */ +/* interrupt-names = "rx"; */ + label = "UART0"; + }; + + + }; +}; \ No newline at end of file diff --git a/framework/CMakeLists.txt b/framework/CMakeLists.txt index b7e6b0250d62b4a18c3bd09738eac8ebdd7d8a56..a090a4f24547382efa7d10f5164a71469b264b7f 100644 --- a/framework/CMakeLists.txt +++ b/framework/CMakeLists.txt @@ -137,9 +137,36 @@ foreach(idx RANGE ${SCP_MODULE_IDX_MAX}) string(APPEND SCP_MODULE_ID_CONST_GEN "static const fwk_id_t fwk_module_id_${SCP_MODULE} = FWK_MODULE_ID_${SCP_MODULE_UPPER}_INIT;\n") string(APPEND SCP_MODULE_EXTERN_GEN "extern const struct fwk_module module_${SCP_MODULE};\n") - string(APPEND SCP_MODULE_EXTERN_CONFIG_GEN "extern const struct fwk_module_config config_${SCP_MODULE};\n") + + # generate dt binding indexes for all modules - allow for x/ref between dt and none dt configured + string(APPEND SCP_MODULE_DT_BINDING_IDX "#define FWK_MODULE_IDX_${SCP_MODULE_UPPER} ${idx}\n") + + + # configuration depends if a module is in one of the DT configured lists + if(SCP_MODULE IN_LIST SCP_DT_CONFIG_MODULES_ALL) + + # common dt module - or platform specific with no dynamic updates + string(APPEND SCP_MODULE_EXTERN_CONFIG_GEN "extern const struct fwk_module_config config_dt_${SCP_MODULE};\n") + string(APPEND SCP_MODULE_CONFIG_GEN " &config_dt_${SCP_MODULE},\n") + + if(SCP_MODULE IN_LIST SCP_DT_CONFIG_MODULES_DYN) + + # dynamic dt config needs additional stuff + string(APPEND SCP_MODULE_DT_DYN_CONFIG "#define FWK_MODULE_GEN_DYNAMIC_${SCP_MODULE_UPPER}\n") + string(APPEND SCP_MODULE_DT_DYN_CONFIG "extern const struct fwk_element *dyn_get_element_table_${SCP_MODULE}(fwk_id_t module_id);\n") + string(APPEND SCP_MODULE_DT_DYN_CONFIG "extern const struct fwk_element *_static_get_element_table_${SCP_MODULE}();\n\n") + + endif() + + else() + + # standard module configuration + string(APPEND SCP_MODULE_EXTERN_CONFIG_GEN "extern const struct fwk_module_config config_${SCP_MODULE};\n") + string(APPEND SCP_MODULE_CONFIG_GEN " &config_${SCP_MODULE},\n") + + endif() + string(APPEND SCP_MODULE_GEN " &module_${SCP_MODULE},\n") - string(APPEND SCP_MODULE_CONFIG_GEN " &config_${SCP_MODULE},\n") # cmake-format: on @@ -157,6 +184,13 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/include/fwk_module_idx.h.in" configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/fwk_module_list.c.in" "${CMAKE_CURRENT_BINARY_DIR}/src/fwk_module_list.c") +# configure device tree related includes +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/include/fwk_dt_config_dyn.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/include/fwk_dt_config_dyn.h") + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/include/fwk_dt_bindings_gen.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/include/fwk_dt_bindings_gen.h") + target_include_directories(framework PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/include") diff --git a/framework/include/dt_sys/util.h b/framework/include/dt_sys/util.h new file mode 100644 index 0000000000000000000000000000000000000000..8c3017fe2ef3db2203ec479498be45d83003cbaf --- /dev/null +++ b/framework/include/dt_sys/util.h @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2011-2014, Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Misc utilities + * + * Misc utilities usable by the kernel and application code. + */ + +#ifndef ZEPHYR_INCLUDE_SYS_UTIL_H_ +#define ZEPHYR_INCLUDE_SYS_UTIL_H_ + +#include + +/* needs to be outside _ASMLANGUAGE so 'true' and 'false' can turn + * into '1' and '0' for asm or linker scripts + */ +#include + +#ifndef _ASMLANGUAGE + +//#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup sys-util Utility Functions + * @{ + */ + +/** @brief Cast @p x, a pointer, to an unsigned integer. */ +#define POINTER_TO_UINT(x) ((uintptr_t) (x)) +/** @brief Cast @p x, an unsigned integer, to a void*. */ +#define UINT_TO_POINTER(x) ((void *) (uintptr_t) (x)) +/** @brief Cast @p x, a pointer, to a signed integer. */ +#define POINTER_TO_INT(x) ((intptr_t) (x)) +/** @brief Cast @p x, a signed integer, to a void*. */ +#define INT_TO_POINTER(x) ((void *) (intptr_t) (x)) + +#if !(defined(__CHAR_BIT__) && defined(__SIZEOF_LONG__)) +# error Missing required predefined macros for BITS_PER_LONG calculation +#endif + +/** Number of bits in a long int. */ +#define BITS_PER_LONG (__CHAR_BIT__ * __SIZEOF_LONG__) + +/** + * @brief Create a contiguous bitmask starting at bit position @p l + * and ending at position @p h. + */ +#define GENMASK(h, l) \ + (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) + +/** @brief 0 if @p cond is true-ish; causes a compile error otherwise. */ +#define ZERO_OR_COMPILE_ERROR(cond) ((int) sizeof(char[1 - 2 * !(cond)]) - 1) + +#if defined(__cplusplus) + +/* The built-in function used below for type checking in C is not + * supported by GNU C++. + */ +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) + +#else /* __cplusplus */ + +/** + * @brief Zero if @p array has an array type, a compile error otherwise + * + * This macro is available only from C, not C++. + */ +#define IS_ARRAY(array) \ + ZERO_OR_COMPILE_ERROR( \ + !__builtin_types_compatible_p(__typeof__(array), \ + __typeof__(&(array)[0]))) + +/** + * @brief Number of elements in the given @p array + * + * In C++, due to language limitations, this will accept as @p array + * any type that implements operator[]. The results may not be + * particulary meaningful in this case. + * + * In C, passing a pointer as @p array causes a compile error. + */ +#define ARRAY_SIZE(array) \ + ((long) (IS_ARRAY(array) + (sizeof(array) / sizeof((array)[0])))) + +#endif /* __cplusplus */ + +/** + * @brief Check if a pointer @p ptr lies within @p array. + * + * In C but not C++, this causes a compile error if @p array is not an array + * (e.g. if @p ptr and @p array are mixed up). + * + * @param ptr a pointer + * @param array an array + * @return 1 if @p ptr is part of @p array, 0 otherwise + */ +#define PART_OF_ARRAY(array, ptr) \ + ((ptr) && ((ptr) >= &array[0] && (ptr) < &array[ARRAY_SIZE(array)])) + +/** + * @brief Get a pointer to a container structure from an element + * + * Example: + * + * struct foo { + * int bar; + * }; + * + * struct foo my_foo; + * int *ptr = &my_foo.bar; + * + * struct foo *container = CONTAINER_OF(ptr, struct foo, bar); + * + * Above, @p container points at @p my_foo. + * + * @param ptr pointer to a structure element + * @param type name of the type that @p ptr is an element of + * @param field the name of the field within the struct @p ptr points to + * @return a pointer to the structure that contains @p ptr + */ +#define CONTAINER_OF(ptr, type, field) \ + ((type *)(((char *)(ptr)) - offsetof(type, field))) + +/** + * @brief Value of @p x rounded up to the next multiple of @p align, + * which must be a power of 2. + */ +#define ROUND_UP(x, align) \ + (((unsigned long)(x) + ((unsigned long)(align) - 1)) & \ + ~((unsigned long)(align) - 1)) + +/** + * @brief Value of @p x rounded down to the previous multiple of @p + * align, which must be a power of 2. + */ +#define ROUND_DOWN(x, align) \ + ((unsigned long)(x) & ~((unsigned long)(align) - 1)) + +/** @brief Value of @p x rounded up to the next word boundary. */ +#define WB_UP(x) ROUND_UP(x, sizeof(void *)) + +/** @brief Value of @p x rounded down to the previous word boundary. */ +#define WB_DN(x) ROUND_DOWN(x, sizeof(void *)) + +/** + * @brief Ceiling function applied to @p numerator / @p divider as a fraction. + */ +#define ceiling_fraction(numerator, divider) \ + (((numerator) + ((divider) - 1)) / (divider)) + +/** + * @def MAX + * @brief The larger value between @p a and @p b. + * @note Arguments are evaluated twice. + */ +#ifndef MAX +/* Use Z_MAX for a GCC-only, single evaluation version */ +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +/** + * @def MIN + * @brief The smaller value between @p a and @p b. + * @note Arguments are evaluated twice. + */ +#ifndef MIN +/* Use Z_MIN for a GCC-only, single evaluation version */ +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +/** + * @def CLAMP + * @brief Clamp a value to a given range. + * @note Arguments are evaluated multiple times. + */ +#ifndef CLAMP +/* Use Z_CLAMP for a GCC-only, single evaluation version */ +#define CLAMP(val, low, high) (((val) <= (low)) ? (low) : MIN(val, high)) +#endif + +/** + * @brief Is @p x a power of two? + * @param x value to check + * @return true if @p x is a power of two, false otherwise + */ +static inline bool is_power_of_two(unsigned int x) +{ + return (x != 0U) && ((x & (x - 1U)) == 0U); +} + +/** + * @brief Arithmetic shift right + * @param value value to shift + * @param shift number of bits to shift + * @return @p value shifted right by @p shift; opened bit positions are + * filled with the sign bit + */ +static inline int64_t arithmetic_shift_right(int64_t value, uint8_t shift) +{ + int64_t sign_ext; + + if (shift == 0U) { + return value; + } + + /* extract sign bit */ + sign_ext = (value >> 63) & 1; + + /* make all bits of sign_ext be the same as the value's sign bit */ + sign_ext = -sign_ext; + + /* shift value and fill opened bit positions with sign bit */ + return (value >> shift) | (sign_ext << (64 - shift)); +} + +/** + * @brief byte by byte memcpy. + * + * Copy `size` bytes of `src` into `dest`. This is guaranteed to be done byte by byte. + * + * @param dst Pointer to the destination memory. + * @param src Pointer to the source of the data. + * @param size The number of bytes to copy. + */ +static inline void bytecpy(void *dst, const void *src, size_t size) +{ + size_t i; + + for (i = 0; i < size; ++i) { + ((uint8_t *)dst)[i] = ((uint8_t *)src)[i]; + } +} + +/** + * @brief Convert a single character into a hexadecimal nibble. + * + * @param c The character to convert + * @param x The address of storage for the converted number. + * + * @return Zero on success or (negative) error code otherwise. + */ +int char2hex(char c, uint8_t *x); + +/** + * @brief Convert a single hexadecimal nibble into a character. + * + * @param c The number to convert + * @param x The address of storage for the converted character. + * + * @return Zero on success or (negative) error code otherwise. + */ +int hex2char(uint8_t x, char *c); + +/** + * @brief Convert a binary array into string representation. + * + * @param buf The binary array to convert + * @param buflen The length of the binary array to convert + * @param hex Address of where to store the string representation. + * @param hexlen Size of the storage area for string representation. + * + * @return The length of the converted string, or 0 if an error occurred. + */ +size_t bin2hex(const uint8_t *buf, size_t buflen, char *hex, size_t hexlen); + +/** + * @brief Convert a hexadecimal string into a binary array. + * + * @param hex The hexadecimal string to convert + * @param hexlen The length of the hexadecimal string to convert. + * @param buf Address of where to store the binary data + * @param buflen Size of the storage area for binary data + * + * @return The length of the binary array, or 0 if an error occurred. + */ +size_t hex2bin(const char *hex, size_t hexlen, uint8_t *buf, size_t buflen); + +/** + * @brief Convert a uint8_t into a decimal string representation. + * + * Convert a uint8_t value into its ASCII decimal string representation. + * The string is terminated if there is enough space in buf. + * + * @param buf Address of where to store the string representation. + * @param buflen Size of the storage area for string representation. + * @param value The value to convert to decimal string + * + * @return The length of the converted string (excluding terminator if + * any), or 0 if an error occurred. + */ +uint8_t u8_to_dec(char *buf, uint8_t buflen, uint8_t value); + +#ifdef __cplusplus +} +#endif + +#endif /* !_ASMLANGUAGE */ + +/** @brief Number of bytes in @p x kibibytes */ +#ifdef _LINKER +/* This is used in linker scripts so need to avoid type casting there */ +#define KB(x) ((x) << 10) +#else +#define KB(x) (((size_t)x) << 10) +#endif +/** @brief Number of bytes in @p x mebibytes */ +#define MB(x) (KB(x) << 10) +/** @brief Number of bytes in @p x gibibytes */ +#define GB(x) (MB(x) << 10) + +/** @brief Number of Hz in @p x kHz */ +#define KHZ(x) ((x) * 1000) +/** @brief Number of Hz in @p x MHz */ +#define MHZ(x) (KHZ(x) * 1000) + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_SYS_UTIL_H_ */ diff --git a/framework/include/dt_sys/util_internal.h b/framework/include/dt_sys/util_internal.h new file mode 100644 index 0000000000000000000000000000000000000000..3b70ec3deb732f2c8f68165bd89455c1b69c3250 --- /dev/null +++ b/framework/include/dt_sys/util_internal.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2011-2014, Wind River Systems, Inc. + * Copyright (c) 2020, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Misc utilities + * + * Repetitive or obscure helper macros needed by sys/util.h. + */ + +#ifndef ZEPHYR_INCLUDE_SYS_UTIL_INTERNAL_H_ +#define ZEPHYR_INCLUDE_SYS_UTIL_INTERNAL_H_ + +#include "util_loops.h" + +/* IS_ENABLED() helpers */ + +/* This is called from IS_ENABLED(), and sticks on a "_XXXX" prefix, + * it will now be "_XXXX1" if config_macro is "1", or just "_XXXX" if it's + * undefined. + * ENABLED: Z_IS_ENABLED2(_XXXX1) + * DISABLED Z_IS_ENABLED2(_XXXX) + */ +#define Z_IS_ENABLED1(config_macro) Z_IS_ENABLED2(_XXXX##config_macro) + +/* Here's the core trick, we map "_XXXX1" to "_YYYY," (i.e. a string + * with a trailing comma), so it has the effect of making this a + * two-argument tuple to the preprocessor only in the case where the + * value is defined to "1" + * ENABLED: _YYYY, <--- note comma! + * DISABLED: _XXXX + */ +#define _XXXX1 _YYYY, + +/* Then we append an extra argument to fool the gcc preprocessor into + * accepting it as a varargs macro. + * arg1 arg2 arg3 + * ENABLED: Z_IS_ENABLED3(_YYYY, 1, 0) + * DISABLED Z_IS_ENABLED3(_XXXX 1, 0) + */ +#define Z_IS_ENABLED2(one_or_two_args) Z_IS_ENABLED3(one_or_two_args 1, 0) + +/* And our second argument is thus now cooked to be 1 in the case + * where the value is defined to 1, and 0 if not: + */ +#define Z_IS_ENABLED3(ignore_this, val, ...) val + +/* Used internally by COND_CODE_1 and COND_CODE_0. */ +#define Z_COND_CODE_1(_flag, _if_1_code, _else_code) \ + __COND_CODE(_XXXX##_flag, _if_1_code, _else_code) +#define Z_COND_CODE_0(_flag, _if_0_code, _else_code) \ + __COND_CODE(_ZZZZ##_flag, _if_0_code, _else_code) +#define _ZZZZ0 _YYYY, +#define __COND_CODE(one_or_two_args, _if_code, _else_code) \ + __GET_ARG2_DEBRACKET(one_or_two_args _if_code, _else_code) + +/* Gets second argument and removes brackets around that argument. It + * is expected that the parameter is provided in brackets/parentheses. + */ +#define __GET_ARG2_DEBRACKET(ignore_this, val, ...) __DEBRACKET val + +/* Used to remove brackets from around a single argument. */ +#define __DEBRACKET(...) __VA_ARGS__ + +/* Used by IS_EMPTY() */ +#define Z_IS_EMPTY_(...) Z_IS_EMPTY__(__VA_ARGS__) +#define Z_IS_EMPTY__(a, ...) Z_IS_EMPTY___(_ZZ##a##ZZ0, __VA_ARGS__) +#define Z_IS_EMPTY___(...) GET_ARG_N(3, __VA_ARGS__) + +/* Used by LIST_DROP_EMPTY() */ +/* Adding ',' after each element would add empty element at the end of + * list, which is hard to remove, so instead precede each element with ',', + * this way first element is empty, and this one is easy to drop. + */ +#define Z_LIST_ADD_ELEM(e) EMPTY, e +#define Z_LIST_DROP_FIRST(...) GET_ARGS_LESS_N(1, __VA_ARGS__) +#define Z_LIST_NO_EMPTIES(e) \ + COND_CODE_1(IS_EMPTY(e), (), (Z_LIST_ADD_ELEM(e))) + +#define UTIL_CAT(a, ...) UTIL_PRIMITIVE_CAT(a, __VA_ARGS__) +#define UTIL_PRIMITIVE_CAT(a, ...) a##__VA_ARGS__ +#define UTIL_CHECK_N(x, n, ...) n +#define UTIL_CHECK(...) UTIL_CHECK_N(__VA_ARGS__, 0,) +#define UTIL_NOT(x) UTIL_CHECK(UTIL_PRIMITIVE_CAT(UTIL_NOT_, x)) +#define UTIL_NOT_0 ~, 1, +#define UTIL_COMPL(b) UTIL_PRIMITIVE_CAT(UTIL_COMPL_, b) +#define UTIL_COMPL_0 1 +#define UTIL_COMPL_1 0 +#define UTIL_BOOL(x) UTIL_COMPL(UTIL_NOT(x)) + +#define UTIL_EVAL(...) __VA_ARGS__ +#define UTIL_EXPAND(...) __VA_ARGS__ +#define UTIL_REPEAT(...) UTIL_LISTIFY(__VA_ARGS__) + +/* Implementation details for NUM_VA_ARGS_LESS_1 */ +#define NUM_VA_ARGS_LESS_1_IMPL( \ + _ignored, \ + _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ + _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ + _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \ + _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \ + _61, _62, N, ...) N + +/* Used by MACRO_MAP_CAT */ +#define MACRO_MAP_CAT_(...) \ + /* To make sure it works also for 2 arguments in total */ \ + MACRO_MAP_CAT_N(NUM_VA_ARGS_LESS_1(__VA_ARGS__), __VA_ARGS__) +#define MACRO_MAP_CAT_N_(N, ...) UTIL_CAT(MACRO_MC_, N)(__VA_ARGS__,) +#define MACRO_MC_0(...) +#define MACRO_MC_1(m, a, ...) m(a) +#define MACRO_MC_2(m, a, ...) UTIL_CAT(m(a), MACRO_MC_1(m, __VA_ARGS__,)) +#define MACRO_MC_3(m, a, ...) UTIL_CAT(m(a), MACRO_MC_2(m, __VA_ARGS__,)) +#define MACRO_MC_4(m, a, ...) UTIL_CAT(m(a), MACRO_MC_3(m, __VA_ARGS__,)) +#define MACRO_MC_5(m, a, ...) UTIL_CAT(m(a), MACRO_MC_4(m, __VA_ARGS__,)) +#define MACRO_MC_6(m, a, ...) UTIL_CAT(m(a), MACRO_MC_5(m, __VA_ARGS__,)) +#define MACRO_MC_7(m, a, ...) UTIL_CAT(m(a), MACRO_MC_6(m, __VA_ARGS__,)) +#define MACRO_MC_8(m, a, ...) UTIL_CAT(m(a), MACRO_MC_7(m, __VA_ARGS__,)) +#define MACRO_MC_9(m, a, ...) UTIL_CAT(m(a), MACRO_MC_8(m, __VA_ARGS__,)) +#define MACRO_MC_10(m, a, ...) UTIL_CAT(m(a), MACRO_MC_9(m, __VA_ARGS__,)) +#define MACRO_MC_11(m, a, ...) UTIL_CAT(m(a), MACRO_MC_10(m, __VA_ARGS__,)) +#define MACRO_MC_12(m, a, ...) UTIL_CAT(m(a), MACRO_MC_11(m, __VA_ARGS__,)) +#define MACRO_MC_13(m, a, ...) UTIL_CAT(m(a), MACRO_MC_12(m, __VA_ARGS__,)) +#define MACRO_MC_14(m, a, ...) UTIL_CAT(m(a), MACRO_MC_13(m, __VA_ARGS__,)) +#define MACRO_MC_15(m, a, ...) UTIL_CAT(m(a), MACRO_MC_14(m, __VA_ARGS__,)) + +#endif /* ZEPHYR_INCLUDE_SYS_UTIL_INTERNAL_H_ */ diff --git a/framework/include/dt_sys/util_loops.h b/framework/include/dt_sys/util_loops.h new file mode 100644 index 0000000000000000000000000000000000000000..7b2e1281ec6a3a37f845a91e3067f2a8920db4b1 --- /dev/null +++ b/framework/include/dt_sys/util_loops.h @@ -0,0 +1,2108 @@ +/* + * Copyright (c) 2021, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Internals for looping macros + * + * Repetitive or obscure helper macros needed by sys/util.h. + */ + +#ifndef ZEPHYR_INCLUDE_SYS_UTIL_LOOPS_H_ +#define ZEPHYR_INCLUDE_SYS_UTIL_LOOPS_H_ + +#define Z_FOR_LOOP_GET_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, \ + _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, \ + _30, _31, _32, _33, _34, _35, _36, _37, _38, \ + _39, _40, _41, _42, _43, _44, _45, _46, _47, \ + _48, _49, _50, _51, _52, _53, _54, _55, _56, \ + _57, _58, _59, _60, _61, _62, _63, _64, N, ...) N + +#define Z_FOR_LOOP_0(z_call, sep, fixed_arg0, fixed_arg1, ...) + +#define Z_FOR_LOOP_1(z_call, sep, fixed_arg0, fixed_arg1, x) \ + z_call(0, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_2(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_1(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(1, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_3(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_2(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(2, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_4(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_3(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(3, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_5(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_4(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(4, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_6(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_5(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(5, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_7(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_6(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(6, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_8(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_7(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(7, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_9(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_8(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(8, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_10(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_9(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(9, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_11(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_10(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(10, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_12(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_11(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(11, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_13(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_12(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(12, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_14(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_13(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(13, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_15(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_14(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(14, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_16(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_15(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(15, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_17(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_16(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(16, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_18(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_17(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(17, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_19(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_18(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(18, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_20(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_19(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(19, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_21(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_20(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(20, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_22(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_21(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(21, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_23(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_22(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(22, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_24(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_23(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(23, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_25(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_24(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(24, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_26(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_25(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(25, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_27(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_26(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(26, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_28(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_27(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(27, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_29(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_28(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(28, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_30(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_29(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(29, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_31(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_30(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(30, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_32(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_31(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(31, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_33(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_32(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(32, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_34(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_33(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(33, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_35(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_34(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(34, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_36(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_35(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(35, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_37(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_36(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(36, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_38(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_37(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(37, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_39(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_38(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(38, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_40(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_39(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(39, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_41(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_40(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(40, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_42(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_41(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(41, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_43(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_42(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(42, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_44(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_43(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(43, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_45(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_44(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(44, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_46(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_45(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(45, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_47(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_46(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(46, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_48(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_47(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(47, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_49(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_48(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(48, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_50(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_49(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(49, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_51(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_50(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(50, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_52(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_51(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(51, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_53(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_52(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(52, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_54(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_53(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(53, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_55(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_54(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(54, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_56(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_55(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(55, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_57(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_56(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(56, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_58(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_57(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(57, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_59(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_58(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(58, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_60(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_59(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(59, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_61(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_60(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(60, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_62(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_61(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(61, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_63(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_62(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(62, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_LOOP_64(z_call, sep, fixed_arg0, fixed_arg1, x, ...) \ + Z_FOR_LOOP_63(z_call, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) \ + __DEBRACKET sep \ + z_call(63, x, fixed_arg0, fixed_arg1) + +#define Z_FOR_EACH_ENGINE(x, sep, fixed_arg0, fixed_arg1, ...) \ + Z_FOR_LOOP_GET_ARG(__VA_ARGS__, \ + Z_FOR_LOOP_64, \ + Z_FOR_LOOP_63, \ + Z_FOR_LOOP_62, \ + Z_FOR_LOOP_61, \ + Z_FOR_LOOP_60, \ + Z_FOR_LOOP_59, \ + Z_FOR_LOOP_58, \ + Z_FOR_LOOP_57, \ + Z_FOR_LOOP_56, \ + Z_FOR_LOOP_55, \ + Z_FOR_LOOP_54, \ + Z_FOR_LOOP_53, \ + Z_FOR_LOOP_52, \ + Z_FOR_LOOP_51, \ + Z_FOR_LOOP_50, \ + Z_FOR_LOOP_49, \ + Z_FOR_LOOP_48, \ + Z_FOR_LOOP_47, \ + Z_FOR_LOOP_46, \ + Z_FOR_LOOP_45, \ + Z_FOR_LOOP_44, \ + Z_FOR_LOOP_43, \ + Z_FOR_LOOP_42, \ + Z_FOR_LOOP_41, \ + Z_FOR_LOOP_40, \ + Z_FOR_LOOP_39, \ + Z_FOR_LOOP_38, \ + Z_FOR_LOOP_37, \ + Z_FOR_LOOP_36, \ + Z_FOR_LOOP_35, \ + Z_FOR_LOOP_34, \ + Z_FOR_LOOP_33, \ + Z_FOR_LOOP_32, \ + Z_FOR_LOOP_31, \ + Z_FOR_LOOP_30, \ + Z_FOR_LOOP_29, \ + Z_FOR_LOOP_28, \ + Z_FOR_LOOP_27, \ + Z_FOR_LOOP_26, \ + Z_FOR_LOOP_25, \ + Z_FOR_LOOP_24, \ + Z_FOR_LOOP_23, \ + Z_FOR_LOOP_22, \ + Z_FOR_LOOP_21, \ + Z_FOR_LOOP_20, \ + Z_FOR_LOOP_19, \ + Z_FOR_LOOP_18, \ + Z_FOR_LOOP_17, \ + Z_FOR_LOOP_16, \ + Z_FOR_LOOP_15, \ + Z_FOR_LOOP_14, \ + Z_FOR_LOOP_13, \ + Z_FOR_LOOP_12, \ + Z_FOR_LOOP_11, \ + Z_FOR_LOOP_10, \ + Z_FOR_LOOP_9, \ + Z_FOR_LOOP_8, \ + Z_FOR_LOOP_7, \ + Z_FOR_LOOP_6, \ + Z_FOR_LOOP_5, \ + Z_FOR_LOOP_4, \ + Z_FOR_LOOP_3, \ + Z_FOR_LOOP_2, \ + Z_FOR_LOOP_1, \ + Z_FOR_LOOP_0)(x, sep, fixed_arg0, fixed_arg1, ##__VA_ARGS__) + +#define Z_GET_ARG_1(_0, ...) _0 + +#define Z_GET_ARG_2(_0, _1, ...) _1 + +#define Z_GET_ARG_3(_0, _1, _2, ...) _2 + +#define Z_GET_ARG_4(_0, _1, _2, _3, ...) _3 + +#define Z_GET_ARG_5(_0, _1, _2, _3, _4, ...) _4 + +#define Z_GET_ARG_6(_0, _1, _2, _3, _4, _5, ...) _5 + +#define Z_GET_ARG_7(_0, _1, _2, _3, _4, _5, _6, ...) _6 + +#define Z_GET_ARG_8(_0, _1, _2, _3, _4, _5, _6, _7, ...) _7 + +#define Z_GET_ARG_9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) _8 + +#define Z_GET_ARG_10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _9 + +#define Z_GET_ARG_11(_0, _1, _2, _3, _4, _5, \ + _6, _7, _8, _9, _10, ...) _10 + +#define Z_GET_ARG_12(_0, _1, _2, _3, _4, _5, _6,\ + _7, _8, _9, _10, _11, ...) _11 + +#define Z_GET_ARG_13(_0, _1, _2, _3, _4, _5, _6, \ + _7, _8, _9, _10, _11, _12, ...) _12 + +#define Z_GET_ARG_14(_0, _1, _2, _3, _4, _5, _6, \ + _7, _8, _9, _10, _11, _12, _13, ...) _13 + +#define Z_GET_ARG_15(_0, _1, _2, _3, _4, _5, _6, _7, \ + _8, _9, _10, _11, _12, _13, _14, ...) _14 + +#define Z_GET_ARG_16(_0, _1, _2, _3, _4, _5, _6, _7, \ + _8, _9, _10, _11, _12, _13, _14, _15, ...) _15 + +#define Z_GET_ARG_17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, ...) _16 + +#define Z_GET_ARG_18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, ...) _17 + +#define Z_GET_ARG_19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, ...) _18 + +#define Z_GET_ARG_20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + ...) _19 + +#define Z_GET_ARG_21(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, ...) _20 + +#define Z_GET_ARG_22(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, ...) _21 + +#define Z_GET_ARG_23(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, ...) _22 + +#define Z_GET_ARG_24(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, ...) _23 + +#define Z_GET_ARG_25(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, ...) _24 + +#define Z_GET_ARG_26(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, ...) _25 + +#define Z_GET_ARG_27(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, ...) _26 + +#define Z_GET_ARG_28(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, ...) _27 + +#define Z_GET_ARG_29(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + ...) _28 + +#define Z_GET_ARG_30(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, ...) _29 + +#define Z_GET_ARG_31(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, ...) _30 + +#define Z_GET_ARG_32(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, ...) _31 + +#define Z_GET_ARG_33(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, ...) _32 + +#define Z_GET_ARG_34(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, ...) _33 + +#define Z_GET_ARG_35(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, ...) _34 + +#define Z_GET_ARG_36(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, ...) _35 + +#define Z_GET_ARG_37(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, ...) _36 + +#define Z_GET_ARG_38(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, ...) _37 + +#define Z_GET_ARG_39(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, ...) _38 + +#define Z_GET_ARG_40(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, ...) _39 + +#define Z_GET_ARG_41(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, ...) _40 + +#define Z_GET_ARG_42(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, ...) _41 + +#define Z_GET_ARG_43(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, ...) _42 + +#define Z_GET_ARG_44(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, ...) _43 + +#define Z_GET_ARG_45(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, ...) _44 + +#define Z_GET_ARG_46(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, ...) _45 + +#define Z_GET_ARG_47(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, ...) _46 + +#define Z_GET_ARG_48(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, ...) _47 + +#define Z_GET_ARG_49(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, ...) _48 + +#define Z_GET_ARG_50(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, ...) _49 + +#define Z_GET_ARG_51(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, ...) _50 + +#define Z_GET_ARG_52(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, ...) _51 + +#define Z_GET_ARG_53(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, ...) _52 + +#define Z_GET_ARG_54(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, ...) _53 + +#define Z_GET_ARG_55(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, ...) _54 + +#define Z_GET_ARG_56(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, ...) _55 + +#define Z_GET_ARG_57(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, ...) _56 + +#define Z_GET_ARG_58(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, _57, ...) _57 + +#define Z_GET_ARG_59(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, _57, _58, ...) _58 + +#define Z_GET_ARG_60(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, _57, _58, _59, ...) _59 + +#define Z_GET_ARG_61(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, _57, _58, _59, _60, ...) _60 + +#define Z_GET_ARG_62(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, _57, _58, _59, _60, _61, ...) _61 + +#define Z_GET_ARG_63(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, _57, _58, _59, _60, _61, _62, ...) _62 + +#define Z_GET_ARG_64(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, _57, _58, _59, _60, _61, _62, _63, ...) _63 + +#define Z_GET_ARGS_LESS_0(...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_1(_0, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_2(_0, _1, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_3(_0, _1, _2, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_4(_0, _1, _2, _3, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_5(_0, _1, _2, _3, _4, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_6(_0, _1, _2, _3, _4, _5, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_7(_0, _1, _2, _3, _4, _5, _6, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_8(_0, _1, _2, _3, _4, _5, \ + _6, _7, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_9(_0, _1, _2, _3, _4, _5, \ + _6, _7, _8, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_10(_0, _1, _2, _3, _4, _5, \ + _6, _7, _8, _9, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_11(_0, _1, _2, _3, _4, _5, \ + _6, _7, _8, _9, _10, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_12(_0, _1, _2, _3, _4, _5, _6,\ + _7, _8, _9, _10, _11, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_13(_0, _1, _2, _3, _4, _5, _6, \ + _7, _8, _9, _10, _11, _12, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_14(_0, _1, _2, _3, _4, _5, _6, \ + _7, _8, _9, _10, _11, _12, _13, \ + ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_15(_0, _1, _2, _3, _4, _5, _6, _7, \ + _8, _9, _10, _11, _12, _13, _14, \ + ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_16(_0, _1, _2, _3, _4, _5, _6, _7, \ + _8, _9, _10, _11, _12, _13, _14, _15, ...) \ + __VA_ARGS__ + +#define Z_GET_ARGS_LESS_17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, ...) \ + __VA_ARGS__ + +#define Z_GET_ARGS_LESS_19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, ...) \ + __VA_ARGS__ + +#define Z_GET_ARGS_LESS_20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_21(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_22(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_23(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_24(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_25(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_26(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_27(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, ...) \ + __VA_ARGS__ + +#define Z_GET_ARGS_LESS_28(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, \ + ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_29(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_30(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_31(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_32(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_33(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_34(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_35(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_36(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_37(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_38(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_39(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_40(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_41(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_42(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_43(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_44(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_45(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_46(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_47(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_48(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_49(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_50(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_51(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_52(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_53(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_54(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_55(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_56(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_57(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_58(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, _57, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_59(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, _57, _58, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_60(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, _57, _58, _59, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_61(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, _57, _58, _59, _60, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_62(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, _57, _58, _59, _60, _61, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_63(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, _57, _58, _59, _60, _61, _62, ...) __VA_ARGS__ + +#define Z_GET_ARGS_LESS_64(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, \ + _29, _30, _31, _32, _33, _34, _35, _36, _37, \ + _38, _39, _40, _41, _42, _43, _44, _45, _46, \ + _47, _48, _49, _50, _51, _52, _53, _54, _55, \ + _56, _57, _58, _59, _60, _61, _62, _63, ...) __VA_ARGS__ + +#define Z_FOR_EACH_IDX_FIXED_ARG_EXEC(idx, x, fixed_arg0, fixed_arg1) \ + fixed_arg0(idx, x, fixed_arg1) + +#define Z_FOR_EACH_IDX_FIXED_ARG(F, sep, fixed_arg, ...) \ + Z_FOR_EACH_ENGINE(Z_FOR_EACH_IDX_FIXED_ARG_EXEC, sep, \ + F, fixed_arg, __VA_ARGS__) + +#define Z_FOR_EACH_FIXED_ARG_EXEC(idx, x, fixed_arg0, fixed_arg1) \ + fixed_arg0(x, fixed_arg1) + +#define Z_FOR_EACH_FIXED_ARG(F, sep, fixed_arg, ...) \ + Z_FOR_EACH_ENGINE(Z_FOR_EACH_FIXED_ARG_EXEC, sep, \ + F, fixed_arg, __VA_ARGS__) + +#define Z_FOR_EACH_IDX_EXEC(idx, x, fixed_arg0, fixed_arg1) \ + fixed_arg0(idx, x) + +#define Z_FOR_EACH_IDX(F, sep, ...) \ + Z_FOR_EACH_ENGINE(Z_FOR_EACH_IDX_EXEC, sep, F, _, __VA_ARGS__) + +#define Z_FOR_EACH_EXEC(idx, x, fixed_arg0, fixed_arg1) \ + fixed_arg0(x) + +#define Z_FOR_EACH(F, sep, ...) \ + Z_FOR_EACH_ENGINE(Z_FOR_EACH_EXEC, sep, F, _, __VA_ARGS__) + +#define Z_BYPASS(x) x + +/* Set of UTIL_LISTIFY particles */ +#define Z_UTIL_LISTIFY_0(F, ...) + +#define Z_UTIL_LISTIFY_1(F, ...) \ + F(0, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_2(F, ...) \ + Z_UTIL_LISTIFY_1(F, __VA_ARGS__) \ + F(1, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_3(F, ...) \ + Z_UTIL_LISTIFY_2(F, __VA_ARGS__) \ + F(2, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_4(F, ...) \ + Z_UTIL_LISTIFY_3(F, __VA_ARGS__) \ + F(3, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_5(F, ...) \ + Z_UTIL_LISTIFY_4(F, __VA_ARGS__) \ + F(4, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_6(F, ...) \ + Z_UTIL_LISTIFY_5(F, __VA_ARGS__) \ + F(5, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_7(F, ...) \ + Z_UTIL_LISTIFY_6(F, __VA_ARGS__) \ + F(6, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_8(F, ...) \ + Z_UTIL_LISTIFY_7(F, __VA_ARGS__) \ + F(7, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_9(F, ...) \ + Z_UTIL_LISTIFY_8(F, __VA_ARGS__) \ + F(8, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_10(F, ...) \ + Z_UTIL_LISTIFY_9(F, __VA_ARGS__) \ + F(9, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_11(F, ...) \ + Z_UTIL_LISTIFY_10(F, __VA_ARGS__) \ + F(10, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_12(F, ...) \ + Z_UTIL_LISTIFY_11(F, __VA_ARGS__) \ + F(11, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_13(F, ...) \ + Z_UTIL_LISTIFY_12(F, __VA_ARGS__) \ + F(12, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_14(F, ...) \ + Z_UTIL_LISTIFY_13(F, __VA_ARGS__) \ + F(13, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_15(F, ...) \ + Z_UTIL_LISTIFY_14(F, __VA_ARGS__) \ + F(14, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_16(F, ...) \ + Z_UTIL_LISTIFY_15(F, __VA_ARGS__) \ + F(15, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_17(F, ...) \ + Z_UTIL_LISTIFY_16(F, __VA_ARGS__) \ + F(16, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_18(F, ...) \ + Z_UTIL_LISTIFY_17(F, __VA_ARGS__) \ + F(17, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_19(F, ...) \ + Z_UTIL_LISTIFY_18(F, __VA_ARGS__) \ + F(18, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_20(F, ...) \ + Z_UTIL_LISTIFY_19(F, __VA_ARGS__) \ + F(19, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_21(F, ...) \ + Z_UTIL_LISTIFY_20(F, __VA_ARGS__) \ + F(20, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_22(F, ...) \ + Z_UTIL_LISTIFY_21(F, __VA_ARGS__) \ + F(21, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_23(F, ...) \ + Z_UTIL_LISTIFY_22(F, __VA_ARGS__) \ + F(22, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_24(F, ...) \ + Z_UTIL_LISTIFY_23(F, __VA_ARGS__) \ + F(23, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_25(F, ...) \ + Z_UTIL_LISTIFY_24(F, __VA_ARGS__) \ + F(24, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_26(F, ...) \ + Z_UTIL_LISTIFY_25(F, __VA_ARGS__) \ + F(25, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_27(F, ...) \ + Z_UTIL_LISTIFY_26(F, __VA_ARGS__) \ + F(26, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_28(F, ...) \ + Z_UTIL_LISTIFY_27(F, __VA_ARGS__) \ + F(27, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_29(F, ...) \ + Z_UTIL_LISTIFY_28(F, __VA_ARGS__) \ + F(28, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_30(F, ...) \ + Z_UTIL_LISTIFY_29(F, __VA_ARGS__) \ + F(29, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_31(F, ...) \ + Z_UTIL_LISTIFY_30(F, __VA_ARGS__) \ + F(30, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_32(F, ...) \ + Z_UTIL_LISTIFY_31(F, __VA_ARGS__) \ + F(31, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_33(F, ...) \ + Z_UTIL_LISTIFY_32(F, __VA_ARGS__) \ + F(32, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_34(F, ...) \ + Z_UTIL_LISTIFY_33(F, __VA_ARGS__) \ + F(33, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_35(F, ...) \ + Z_UTIL_LISTIFY_34(F, __VA_ARGS__) \ + F(34, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_36(F, ...) \ + Z_UTIL_LISTIFY_35(F, __VA_ARGS__) \ + F(35, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_37(F, ...) \ + Z_UTIL_LISTIFY_36(F, __VA_ARGS__) \ + F(36, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_38(F, ...) \ + Z_UTIL_LISTIFY_37(F, __VA_ARGS__) \ + F(37, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_39(F, ...) \ + Z_UTIL_LISTIFY_38(F, __VA_ARGS__) \ + F(38, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_40(F, ...) \ + Z_UTIL_LISTIFY_39(F, __VA_ARGS__) \ + F(39, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_41(F, ...) \ + Z_UTIL_LISTIFY_40(F, __VA_ARGS__) \ + F(40, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_42(F, ...) \ + Z_UTIL_LISTIFY_41(F, __VA_ARGS__) \ + F(41, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_43(F, ...) \ + Z_UTIL_LISTIFY_42(F, __VA_ARGS__) \ + F(42, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_44(F, ...) \ + Z_UTIL_LISTIFY_43(F, __VA_ARGS__) \ + F(43, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_45(F, ...) \ + Z_UTIL_LISTIFY_44(F, __VA_ARGS__) \ + F(44, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_46(F, ...) \ + Z_UTIL_LISTIFY_45(F, __VA_ARGS__) \ + F(45, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_47(F, ...) \ + Z_UTIL_LISTIFY_46(F, __VA_ARGS__) \ + F(46, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_48(F, ...) \ + Z_UTIL_LISTIFY_47(F, __VA_ARGS__) \ + F(47, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_49(F, ...) \ + Z_UTIL_LISTIFY_48(F, __VA_ARGS__) \ + F(48, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_50(F, ...) \ + Z_UTIL_LISTIFY_49(F, __VA_ARGS__) \ + F(49, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_51(F, ...) \ + Z_UTIL_LISTIFY_50(F, __VA_ARGS__) \ + F(50, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_52(F, ...) \ + Z_UTIL_LISTIFY_51(F, __VA_ARGS__) \ + F(51, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_53(F, ...) \ + Z_UTIL_LISTIFY_52(F, __VA_ARGS__) \ + F(52, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_54(F, ...) \ + Z_UTIL_LISTIFY_53(F, __VA_ARGS__) \ + F(53, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_55(F, ...) \ + Z_UTIL_LISTIFY_54(F, __VA_ARGS__) \ + F(54, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_56(F, ...) \ + Z_UTIL_LISTIFY_55(F, __VA_ARGS__) \ + F(55, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_57(F, ...) \ + Z_UTIL_LISTIFY_56(F, __VA_ARGS__) \ + F(56, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_58(F, ...) \ + Z_UTIL_LISTIFY_57(F, __VA_ARGS__) \ + F(57, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_59(F, ...) \ + Z_UTIL_LISTIFY_58(F, __VA_ARGS__) \ + F(58, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_60(F, ...) \ + Z_UTIL_LISTIFY_59(F, __VA_ARGS__) \ + F(59, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_61(F, ...) \ + Z_UTIL_LISTIFY_60(F, __VA_ARGS__) \ + F(60, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_62(F, ...) \ + Z_UTIL_LISTIFY_61(F, __VA_ARGS__) \ + F(61, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_63(F, ...) \ + Z_UTIL_LISTIFY_62(F, __VA_ARGS__) \ + F(62, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_64(F, ...) \ + Z_UTIL_LISTIFY_63(F, __VA_ARGS__) \ + F(63, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_65(F, ...) \ + Z_UTIL_LISTIFY_64(F, __VA_ARGS__) \ + F(64, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_66(F, ...) \ + Z_UTIL_LISTIFY_65(F, __VA_ARGS__) \ + F(65, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_67(F, ...) \ + Z_UTIL_LISTIFY_66(F, __VA_ARGS__) \ + F(66, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_68(F, ...) \ + Z_UTIL_LISTIFY_67(F, __VA_ARGS__) \ + F(67, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_69(F, ...) \ + Z_UTIL_LISTIFY_68(F, __VA_ARGS__) \ + F(68, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_70(F, ...) \ + Z_UTIL_LISTIFY_69(F, __VA_ARGS__) \ + F(69, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_71(F, ...) \ + Z_UTIL_LISTIFY_70(F, __VA_ARGS__) \ + F(70, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_72(F, ...) \ + Z_UTIL_LISTIFY_71(F, __VA_ARGS__) \ + F(71, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_73(F, ...) \ + Z_UTIL_LISTIFY_72(F, __VA_ARGS__) \ + F(72, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_74(F, ...) \ + Z_UTIL_LISTIFY_73(F, __VA_ARGS__) \ + F(73, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_75(F, ...) \ + Z_UTIL_LISTIFY_74(F, __VA_ARGS__) \ + F(74, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_76(F, ...) \ + Z_UTIL_LISTIFY_75(F, __VA_ARGS__) \ + F(75, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_77(F, ...) \ + Z_UTIL_LISTIFY_76(F, __VA_ARGS__) \ + F(76, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_78(F, ...) \ + Z_UTIL_LISTIFY_77(F, __VA_ARGS__) \ + F(77, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_79(F, ...) \ + Z_UTIL_LISTIFY_78(F, __VA_ARGS__) \ + F(78, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_80(F, ...) \ + Z_UTIL_LISTIFY_79(F, __VA_ARGS__) \ + F(79, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_81(F, ...) \ + Z_UTIL_LISTIFY_80(F, __VA_ARGS__) \ + F(80, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_82(F, ...) \ + Z_UTIL_LISTIFY_81(F, __VA_ARGS__) \ + F(81, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_83(F, ...) \ + Z_UTIL_LISTIFY_82(F, __VA_ARGS__) \ + F(82, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_84(F, ...) \ + Z_UTIL_LISTIFY_83(F, __VA_ARGS__) \ + F(83, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_85(F, ...) \ + Z_UTIL_LISTIFY_84(F, __VA_ARGS__) \ + F(84, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_86(F, ...) \ + Z_UTIL_LISTIFY_85(F, __VA_ARGS__) \ + F(85, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_87(F, ...) \ + Z_UTIL_LISTIFY_86(F, __VA_ARGS__) \ + F(86, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_88(F, ...) \ + Z_UTIL_LISTIFY_87(F, __VA_ARGS__) \ + F(87, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_89(F, ...) \ + Z_UTIL_LISTIFY_88(F, __VA_ARGS__) \ + F(88, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_90(F, ...) \ + Z_UTIL_LISTIFY_89(F, __VA_ARGS__) \ + F(89, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_91(F, ...) \ + Z_UTIL_LISTIFY_90(F, __VA_ARGS__) \ + F(90, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_92(F, ...) \ + Z_UTIL_LISTIFY_91(F, __VA_ARGS__) \ + F(91, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_93(F, ...) \ + Z_UTIL_LISTIFY_92(F, __VA_ARGS__) \ + F(92, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_94(F, ...) \ + Z_UTIL_LISTIFY_93(F, __VA_ARGS__) \ + F(93, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_95(F, ...) \ + Z_UTIL_LISTIFY_94(F, __VA_ARGS__) \ + F(94, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_96(F, ...) \ + Z_UTIL_LISTIFY_95(F, __VA_ARGS__) \ + F(95, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_97(F, ...) \ + Z_UTIL_LISTIFY_96(F, __VA_ARGS__) \ + F(96, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_98(F, ...) \ + Z_UTIL_LISTIFY_97(F, __VA_ARGS__) \ + F(97, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_99(F, ...) \ + Z_UTIL_LISTIFY_98(F, __VA_ARGS__) \ + F(98, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_100(F, ...) \ + Z_UTIL_LISTIFY_99(F, __VA_ARGS__) \ + F(99, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_101(F, ...) \ + Z_UTIL_LISTIFY_100(F, __VA_ARGS__) \ + F(100, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_102(F, ...) \ + Z_UTIL_LISTIFY_101(F, __VA_ARGS__) \ + F(101, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_103(F, ...) \ + Z_UTIL_LISTIFY_102(F, __VA_ARGS__) \ + F(102, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_104(F, ...) \ + Z_UTIL_LISTIFY_103(F, __VA_ARGS__) \ + F(103, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_105(F, ...) \ + Z_UTIL_LISTIFY_104(F, __VA_ARGS__) \ + F(104, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_106(F, ...) \ + Z_UTIL_LISTIFY_105(F, __VA_ARGS__) \ + F(105, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_107(F, ...) \ + Z_UTIL_LISTIFY_106(F, __VA_ARGS__) \ + F(106, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_108(F, ...) \ + Z_UTIL_LISTIFY_107(F, __VA_ARGS__) \ + F(107, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_109(F, ...) \ + Z_UTIL_LISTIFY_108(F, __VA_ARGS__) \ + F(108, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_110(F, ...) \ + Z_UTIL_LISTIFY_109(F, __VA_ARGS__) \ + F(109, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_111(F, ...) \ + Z_UTIL_LISTIFY_110(F, __VA_ARGS__) \ + F(110, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_112(F, ...) \ + Z_UTIL_LISTIFY_111(F, __VA_ARGS__) \ + F(111, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_113(F, ...) \ + Z_UTIL_LISTIFY_112(F, __VA_ARGS__) \ + F(112, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_114(F, ...) \ + Z_UTIL_LISTIFY_113(F, __VA_ARGS__) \ + F(113, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_115(F, ...) \ + Z_UTIL_LISTIFY_114(F, __VA_ARGS__) \ + F(114, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_116(F, ...) \ + Z_UTIL_LISTIFY_115(F, __VA_ARGS__) \ + F(115, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_117(F, ...) \ + Z_UTIL_LISTIFY_116(F, __VA_ARGS__) \ + F(116, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_118(F, ...) \ + Z_UTIL_LISTIFY_117(F, __VA_ARGS__) \ + F(117, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_119(F, ...) \ + Z_UTIL_LISTIFY_118(F, __VA_ARGS__) \ + F(118, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_120(F, ...) \ + Z_UTIL_LISTIFY_119(F, __VA_ARGS__) \ + F(119, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_121(F, ...) \ + Z_UTIL_LISTIFY_120(F, __VA_ARGS__) \ + F(120, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_122(F, ...) \ + Z_UTIL_LISTIFY_121(F, __VA_ARGS__) \ + F(121, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_123(F, ...) \ + Z_UTIL_LISTIFY_122(F, __VA_ARGS__) \ + F(122, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_124(F, ...) \ + Z_UTIL_LISTIFY_123(F, __VA_ARGS__) \ + F(123, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_125(F, ...) \ + Z_UTIL_LISTIFY_124(F, __VA_ARGS__) \ + F(124, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_126(F, ...) \ + Z_UTIL_LISTIFY_125(F, __VA_ARGS__) \ + F(125, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_127(F, ...) \ + Z_UTIL_LISTIFY_126(F, __VA_ARGS__) \ + F(126, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_128(F, ...) \ + Z_UTIL_LISTIFY_127(F, __VA_ARGS__) \ + F(127, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_129(F, ...) \ + Z_UTIL_LISTIFY_128(F, __VA_ARGS__) \ + F(128, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_130(F, ...) \ + Z_UTIL_LISTIFY_129(F, __VA_ARGS__) \ + F(129, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_131(F, ...) \ + Z_UTIL_LISTIFY_130(F, __VA_ARGS__) \ + F(130, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_132(F, ...) \ + Z_UTIL_LISTIFY_131(F, __VA_ARGS__) \ + F(131, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_133(F, ...) \ + Z_UTIL_LISTIFY_132(F, __VA_ARGS__) \ + F(132, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_134(F, ...) \ + Z_UTIL_LISTIFY_133(F, __VA_ARGS__) \ + F(133, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_135(F, ...) \ + Z_UTIL_LISTIFY_134(F, __VA_ARGS__) \ + F(134, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_136(F, ...) \ + Z_UTIL_LISTIFY_135(F, __VA_ARGS__) \ + F(135, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_137(F, ...) \ + Z_UTIL_LISTIFY_136(F, __VA_ARGS__) \ + F(136, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_138(F, ...) \ + Z_UTIL_LISTIFY_137(F, __VA_ARGS__) \ + F(137, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_139(F, ...) \ + Z_UTIL_LISTIFY_138(F, __VA_ARGS__) \ + F(138, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_140(F, ...) \ + Z_UTIL_LISTIFY_139(F, __VA_ARGS__) \ + F(139, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_141(F, ...) \ + Z_UTIL_LISTIFY_140(F, __VA_ARGS__) \ + F(140, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_142(F, ...) \ + Z_UTIL_LISTIFY_141(F, __VA_ARGS__) \ + F(141, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_143(F, ...) \ + Z_UTIL_LISTIFY_142(F, __VA_ARGS__) \ + F(142, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_144(F, ...) \ + Z_UTIL_LISTIFY_143(F, __VA_ARGS__) \ + F(143, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_145(F, ...) \ + Z_UTIL_LISTIFY_144(F, __VA_ARGS__) \ + F(144, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_146(F, ...) \ + Z_UTIL_LISTIFY_145(F, __VA_ARGS__) \ + F(145, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_147(F, ...) \ + Z_UTIL_LISTIFY_146(F, __VA_ARGS__) \ + F(146, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_148(F, ...) \ + Z_UTIL_LISTIFY_147(F, __VA_ARGS__) \ + F(147, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_149(F, ...) \ + Z_UTIL_LISTIFY_148(F, __VA_ARGS__) \ + F(148, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_150(F, ...) \ + Z_UTIL_LISTIFY_149(F, __VA_ARGS__) \ + F(149, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_151(F, ...) \ + Z_UTIL_LISTIFY_150(F, __VA_ARGS__) \ + F(150, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_152(F, ...) \ + Z_UTIL_LISTIFY_151(F, __VA_ARGS__) \ + F(151, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_153(F, ...) \ + Z_UTIL_LISTIFY_152(F, __VA_ARGS__) \ + F(152, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_154(F, ...) \ + Z_UTIL_LISTIFY_153(F, __VA_ARGS__) \ + F(153, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_155(F, ...) \ + Z_UTIL_LISTIFY_154(F, __VA_ARGS__) \ + F(154, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_156(F, ...) \ + Z_UTIL_LISTIFY_155(F, __VA_ARGS__) \ + F(155, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_157(F, ...) \ + Z_UTIL_LISTIFY_156(F, __VA_ARGS__) \ + F(156, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_158(F, ...) \ + Z_UTIL_LISTIFY_157(F, __VA_ARGS__) \ + F(157, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_159(F, ...) \ + Z_UTIL_LISTIFY_158(F, __VA_ARGS__) \ + F(158, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_160(F, ...) \ + Z_UTIL_LISTIFY_159(F, __VA_ARGS__) \ + F(159, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_161(F, ...) \ + Z_UTIL_LISTIFY_160(F, __VA_ARGS__) \ + F(160, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_162(F, ...) \ + Z_UTIL_LISTIFY_161(F, __VA_ARGS__) \ + F(161, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_163(F, ...) \ + Z_UTIL_LISTIFY_162(F, __VA_ARGS__) \ + F(162, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_164(F, ...) \ + Z_UTIL_LISTIFY_163(F, __VA_ARGS__) \ + F(163, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_165(F, ...) \ + Z_UTIL_LISTIFY_164(F, __VA_ARGS__) \ + F(164, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_166(F, ...) \ + Z_UTIL_LISTIFY_165(F, __VA_ARGS__) \ + F(165, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_167(F, ...) \ + Z_UTIL_LISTIFY_166(F, __VA_ARGS__) \ + F(166, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_168(F, ...) \ + Z_UTIL_LISTIFY_167(F, __VA_ARGS__) \ + F(167, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_169(F, ...) \ + Z_UTIL_LISTIFY_168(F, __VA_ARGS__) \ + F(168, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_170(F, ...) \ + Z_UTIL_LISTIFY_169(F, __VA_ARGS__) \ + F(169, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_171(F, ...) \ + Z_UTIL_LISTIFY_170(F, __VA_ARGS__) \ + F(170, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_172(F, ...) \ + Z_UTIL_LISTIFY_171(F, __VA_ARGS__) \ + F(171, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_173(F, ...) \ + Z_UTIL_LISTIFY_172(F, __VA_ARGS__) \ + F(172, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_174(F, ...) \ + Z_UTIL_LISTIFY_173(F, __VA_ARGS__) \ + F(173, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_175(F, ...) \ + Z_UTIL_LISTIFY_174(F, __VA_ARGS__) \ + F(174, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_176(F, ...) \ + Z_UTIL_LISTIFY_175(F, __VA_ARGS__) \ + F(175, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_177(F, ...) \ + Z_UTIL_LISTIFY_176(F, __VA_ARGS__) \ + F(176, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_178(F, ...) \ + Z_UTIL_LISTIFY_177(F, __VA_ARGS__) \ + F(177, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_179(F, ...) \ + Z_UTIL_LISTIFY_178(F, __VA_ARGS__) \ + F(178, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_180(F, ...) \ + Z_UTIL_LISTIFY_179(F, __VA_ARGS__) \ + F(179, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_181(F, ...) \ + Z_UTIL_LISTIFY_180(F, __VA_ARGS__) \ + F(180, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_182(F, ...) \ + Z_UTIL_LISTIFY_181(F, __VA_ARGS__) \ + F(181, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_183(F, ...) \ + Z_UTIL_LISTIFY_182(F, __VA_ARGS__) \ + F(182, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_184(F, ...) \ + Z_UTIL_LISTIFY_183(F, __VA_ARGS__) \ + F(183, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_185(F, ...) \ + Z_UTIL_LISTIFY_184(F, __VA_ARGS__) \ + F(184, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_186(F, ...) \ + Z_UTIL_LISTIFY_185(F, __VA_ARGS__) \ + F(185, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_187(F, ...) \ + Z_UTIL_LISTIFY_186(F, __VA_ARGS__) \ + F(186, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_188(F, ...) \ + Z_UTIL_LISTIFY_187(F, __VA_ARGS__) \ + F(187, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_189(F, ...) \ + Z_UTIL_LISTIFY_188(F, __VA_ARGS__) \ + F(188, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_190(F, ...) \ + Z_UTIL_LISTIFY_189(F, __VA_ARGS__) \ + F(189, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_191(F, ...) \ + Z_UTIL_LISTIFY_190(F, __VA_ARGS__) \ + F(190, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_192(F, ...) \ + Z_UTIL_LISTIFY_191(F, __VA_ARGS__) \ + F(191, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_193(F, ...) \ + Z_UTIL_LISTIFY_192(F, __VA_ARGS__) \ + F(192, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_194(F, ...) \ + Z_UTIL_LISTIFY_193(F, __VA_ARGS__) \ + F(193, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_195(F, ...) \ + Z_UTIL_LISTIFY_194(F, __VA_ARGS__) \ + F(194, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_196(F, ...) \ + Z_UTIL_LISTIFY_195(F, __VA_ARGS__) \ + F(195, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_197(F, ...) \ + Z_UTIL_LISTIFY_196(F, __VA_ARGS__) \ + F(196, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_198(F, ...) \ + Z_UTIL_LISTIFY_197(F, __VA_ARGS__) \ + F(197, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_199(F, ...) \ + Z_UTIL_LISTIFY_198(F, __VA_ARGS__) \ + F(198, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_200(F, ...) \ + Z_UTIL_LISTIFY_199(F, __VA_ARGS__) \ + F(199, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_201(F, ...) \ + Z_UTIL_LISTIFY_200(F, __VA_ARGS__) \ + F(200, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_202(F, ...) \ + Z_UTIL_LISTIFY_201(F, __VA_ARGS__) \ + F(201, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_203(F, ...) \ + Z_UTIL_LISTIFY_202(F, __VA_ARGS__) \ + F(202, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_204(F, ...) \ + Z_UTIL_LISTIFY_203(F, __VA_ARGS__) \ + F(203, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_205(F, ...) \ + Z_UTIL_LISTIFY_204(F, __VA_ARGS__) \ + F(204, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_206(F, ...) \ + Z_UTIL_LISTIFY_205(F, __VA_ARGS__) \ + F(205, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_207(F, ...) \ + Z_UTIL_LISTIFY_206(F, __VA_ARGS__) \ + F(206, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_208(F, ...) \ + Z_UTIL_LISTIFY_207(F, __VA_ARGS__) \ + F(207, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_209(F, ...) \ + Z_UTIL_LISTIFY_208(F, __VA_ARGS__) \ + F(208, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_210(F, ...) \ + Z_UTIL_LISTIFY_209(F, __VA_ARGS__) \ + F(209, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_211(F, ...) \ + Z_UTIL_LISTIFY_210(F, __VA_ARGS__) \ + F(210, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_212(F, ...) \ + Z_UTIL_LISTIFY_211(F, __VA_ARGS__) \ + F(211, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_213(F, ...) \ + Z_UTIL_LISTIFY_212(F, __VA_ARGS__) \ + F(212, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_214(F, ...) \ + Z_UTIL_LISTIFY_213(F, __VA_ARGS__) \ + F(213, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_215(F, ...) \ + Z_UTIL_LISTIFY_214(F, __VA_ARGS__) \ + F(214, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_216(F, ...) \ + Z_UTIL_LISTIFY_215(F, __VA_ARGS__) \ + F(215, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_217(F, ...) \ + Z_UTIL_LISTIFY_216(F, __VA_ARGS__) \ + F(216, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_218(F, ...) \ + Z_UTIL_LISTIFY_217(F, __VA_ARGS__) \ + F(217, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_219(F, ...) \ + Z_UTIL_LISTIFY_218(F, __VA_ARGS__) \ + F(218, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_220(F, ...) \ + Z_UTIL_LISTIFY_219(F, __VA_ARGS__) \ + F(219, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_221(F, ...) \ + Z_UTIL_LISTIFY_220(F, __VA_ARGS__) \ + F(220, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_222(F, ...) \ + Z_UTIL_LISTIFY_221(F, __VA_ARGS__) \ + F(221, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_223(F, ...) \ + Z_UTIL_LISTIFY_222(F, __VA_ARGS__) \ + F(222, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_224(F, ...) \ + Z_UTIL_LISTIFY_223(F, __VA_ARGS__) \ + F(223, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_225(F, ...) \ + Z_UTIL_LISTIFY_224(F, __VA_ARGS__) \ + F(224, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_226(F, ...) \ + Z_UTIL_LISTIFY_225(F, __VA_ARGS__) \ + F(225, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_227(F, ...) \ + Z_UTIL_LISTIFY_226(F, __VA_ARGS__) \ + F(226, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_228(F, ...) \ + Z_UTIL_LISTIFY_227(F, __VA_ARGS__) \ + F(227, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_229(F, ...) \ + Z_UTIL_LISTIFY_228(F, __VA_ARGS__) \ + F(228, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_230(F, ...) \ + Z_UTIL_LISTIFY_229(F, __VA_ARGS__) \ + F(229, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_231(F, ...) \ + Z_UTIL_LISTIFY_230(F, __VA_ARGS__) \ + F(230, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_232(F, ...) \ + Z_UTIL_LISTIFY_231(F, __VA_ARGS__) \ + F(231, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_233(F, ...) \ + Z_UTIL_LISTIFY_232(F, __VA_ARGS__) \ + F(232, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_234(F, ...) \ + Z_UTIL_LISTIFY_233(F, __VA_ARGS__) \ + F(233, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_235(F, ...) \ + Z_UTIL_LISTIFY_234(F, __VA_ARGS__) \ + F(234, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_236(F, ...) \ + Z_UTIL_LISTIFY_235(F, __VA_ARGS__) \ + F(235, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_237(F, ...) \ + Z_UTIL_LISTIFY_236(F, __VA_ARGS__) \ + F(236, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_238(F, ...) \ + Z_UTIL_LISTIFY_237(F, __VA_ARGS__) \ + F(237, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_239(F, ...) \ + Z_UTIL_LISTIFY_238(F, __VA_ARGS__) \ + F(238, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_240(F, ...) \ + Z_UTIL_LISTIFY_239(F, __VA_ARGS__) \ + F(239, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_241(F, ...) \ + Z_UTIL_LISTIFY_240(F, __VA_ARGS__) \ + F(240, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_242(F, ...) \ + Z_UTIL_LISTIFY_241(F, __VA_ARGS__) \ + F(241, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_243(F, ...) \ + Z_UTIL_LISTIFY_242(F, __VA_ARGS__) \ + F(242, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_244(F, ...) \ + Z_UTIL_LISTIFY_243(F, __VA_ARGS__) \ + F(243, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_245(F, ...) \ + Z_UTIL_LISTIFY_244(F, __VA_ARGS__) \ + F(244, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_246(F, ...) \ + Z_UTIL_LISTIFY_245(F, __VA_ARGS__) \ + F(245, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_247(F, ...) \ + Z_UTIL_LISTIFY_246(F, __VA_ARGS__) \ + F(246, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_248(F, ...) \ + Z_UTIL_LISTIFY_247(F, __VA_ARGS__) \ + F(247, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_249(F, ...) \ + Z_UTIL_LISTIFY_248(F, __VA_ARGS__) \ + F(248, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_250(F, ...) \ + Z_UTIL_LISTIFY_249(F, __VA_ARGS__) \ + F(249, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_251(F, ...) \ + Z_UTIL_LISTIFY_250(F, __VA_ARGS__) \ + F(250, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_252(F, ...) \ + Z_UTIL_LISTIFY_251(F, __VA_ARGS__) \ + F(251, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_253(F, ...) \ + Z_UTIL_LISTIFY_252(F, __VA_ARGS__) \ + F(252, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_254(F, ...) \ + Z_UTIL_LISTIFY_253(F, __VA_ARGS__) \ + F(253, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_255(F, ...) \ + Z_UTIL_LISTIFY_254(F, __VA_ARGS__) \ + F(254, __VA_ARGS__) + +#define Z_UTIL_LISTIFY_256(F, ...) \ + Z_UTIL_LISTIFY_255(F, __VA_ARGS__) \ + F(255, __VA_ARGS__) + +#endif /* ZEPHYR_INCLUDE_SYS_UTIL_LOOPS_H_ */ diff --git a/framework/include/dt_sys/util_macro.h b/framework/include/dt_sys/util_macro.h new file mode 100644 index 0000000000000000000000000000000000000000..5f81be0b894794ab018126ba19461bad9a69f018 --- /dev/null +++ b/framework/include/dt_sys/util_macro.h @@ -0,0 +1,610 @@ +/* + * Copyright (c) 2011-2014, Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Macro utilities + * + * Macro utilities are the public interface for C/C++ code and device tree + * related implementation. In general, C/C++ will include + * instead this file directly. For device tree implementation, this file + * should be include instead + */ + +#ifndef ZEPHYR_INCLUDE_SYS_UTIL_MACROS_H_ +#define ZEPHYR_INCLUDE_SYS_UTIL_MACROS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup sys-util + * @{ + */ + +/* + * Most of the eldritch implementation details for all the macrobatics + * below (APIs like IS_ENABLED(), COND_CODE_1(), etc.) are hidden away + * in this file. + */ +#include + +#ifndef BIT +#if defined(_ASMLANGUAGE) +#define BIT(n) (1 << (n)) +#else +/** + * @brief Unsigned integer with bit position @p n set (signed in + * assembly language). + */ +#define BIT(n) (1UL << (n)) +#endif +#endif + +/** @brief 64-bit unsigned integer with bit position @p _n set. */ +#define BIT64(_n) (1ULL << (_n)) + +/** + * @brief Set or clear a bit depending on a boolean value + * + * The argument @p var is a variable whose value is written to as a + * side effect. + * + * @param var Variable to be altered + * @param bit Bit number + * @param set if 0, clears @p bit in @p var; any other value sets @p bit + */ +#define WRITE_BIT(var, bit, set) \ + ((var) = (set) ? ((var) | BIT(bit)) : ((var) & ~BIT(bit))) + +/** + * @brief Bit mask with bits 0 through n-1 (inclusive) set, + * or 0 if @p n is 0. + */ +#define BIT_MASK(n) (BIT(n) - 1UL) + +/** + * @brief 64-bit bit mask with bits 0 through n-1 (inclusive) set, + * or 0 if @p n is 0. + */ +#define BIT64_MASK(n) (BIT64(n) - 1ULL) + +/** + * @brief Check for macro definition in compiler-visible expressions + * + * This trick was pioneered in Linux as the config_enabled() macro. It + * has the effect of taking a macro value that may be defined to "1" + * or may not be defined at all and turning it into a literal + * expression that can be handled by the C compiler instead of just + * the preprocessor. It is often used with a @p CONFIG_FOO macro which + * may be defined to 1 via Kconfig, or left undefined. + * + * That is, it works similarly to \#if defined(CONFIG_FOO) + * except that its expansion is a C expression. Thus, much \#ifdef + * usage can be replaced with equivalents like: + * + * if (IS_ENABLED(CONFIG_FOO)) { + * do_something_with_foo + * } + * + * This is cleaner since the compiler can generate errors and warnings + * for @p do_something_with_foo even when @p CONFIG_FOO is undefined. + * + * @param config_macro Macro to check + * @return 1 if @p config_macro is defined to 1, 0 otherwise (including + * if @p config_macro is not defined) + */ +#define IS_ENABLED(config_macro) Z_IS_ENABLED1(config_macro) +/* INTERNAL: the first pass above is just to expand any existing + * macros, we need the macro value to be e.g. a literal "1" at + * expansion time in the next macro, not "(1)", etc... Standard + * recursive expansion does not work. + */ + +/** + * @brief Insert code depending on whether @p _flag expands to 1 or not. + * + * This relies on similar tricks as IS_ENABLED(), but as the result of + * @p _flag expansion, results in either @p _if_1_code or @p + * _else_code is expanded. + * + * To prevent the preprocessor from treating commas as argument + * separators, the @p _if_1_code and @p _else_code expressions must be + * inside brackets/parentheses: (). These are stripped away + * during macro expansion. + * + * Example: + * + * COND_CODE_1(CONFIG_FLAG, (uint32_t x;), (there_is_no_flag();)) + * + * If @p CONFIG_FLAG is defined to 1, this expands to: + * + * uint32_t x; + * + * It expands to there_is_no_flag(); otherwise. + * + * This could be used as an alternative to: + * + * #if defined(CONFIG_FLAG) && (CONFIG_FLAG == 1) + * #define MAYBE_DECLARE(x) uint32_t x + * #else + * #define MAYBE_DECLARE(x) there_is_no_flag() + * #endif + * + * MAYBE_DECLARE(x); + * + * However, the advantage of COND_CODE_1() is that code is resolved in + * place where it is used, while the @p \#if method defines @p + * MAYBE_DECLARE on two lines and requires it to be invoked again on a + * separate line. This makes COND_CODE_1() more concise and also + * sometimes more useful when used within another macro's expansion. + * + * @note @p _flag can be the result of preprocessor expansion, e.g. + * an expression involving NUM_VA_ARGS_LESS_1(...). + * However, @p _if_1_code is only expanded if @p _flag expands + * to the integer literal 1. Integer expressions that evaluate + * to 1, e.g. after doing some arithmetic, will not work. + * + * @param _flag evaluated flag + * @param _if_1_code result if @p _flag expands to 1; must be in parentheses + * @param _else_code result otherwise; must be in parentheses + */ +#define COND_CODE_1(_flag, _if_1_code, _else_code) \ + Z_COND_CODE_1(_flag, _if_1_code, _else_code) + +/** + * @brief Like COND_CODE_1() except tests if @p _flag is 0. + * + * This is like COND_CODE_1(), except that it tests whether @p _flag + * expands to the integer literal 0. It expands to @p _if_0_code if + * so, and @p _else_code otherwise; both of these must be enclosed in + * parentheses. + * + * @param _flag evaluated flag + * @param _if_0_code result if @p _flag expands to 0; must be in parentheses + * @param _else_code result otherwise; must be in parentheses + * @see COND_CODE_1() + */ +#define COND_CODE_0(_flag, _if_0_code, _else_code) \ + Z_COND_CODE_0(_flag, _if_0_code, _else_code) + +/** + * @brief Insert code if @p _flag is defined and equals 1. + * + * Like COND_CODE_1(), this expands to @p _code if @p _flag is defined to 1; + * it expands to nothing otherwise. + * + * Example: + * + * IF_ENABLED(CONFIG_FLAG, (uint32_t foo;)) + * + * If @p CONFIG_FLAG is defined to 1, this expands to: + * + * uint32_t foo; + * + * and to nothing otherwise. + * + * It can be considered as a more compact alternative to: + * + * #if defined(CONFIG_FLAG) && (CONFIG_FLAG == 1) + * uint32_t foo; + * #endif + * + * @param _flag evaluated flag + * @param _code result if @p _flag expands to 1; must be in parentheses + */ +#define IF_ENABLED(_flag, _code) \ + COND_CODE_1(_flag, _code, ()) + +/** + * @brief Check if a macro has a replacement expression + * + * If @p a is a macro defined to a nonempty value, this will return + * true, otherwise it will return false. It only works with defined + * macros, so an additional @p \#ifdef test may be needed in some cases. + * + * This macro may be used with COND_CODE_1() and COND_CODE_0() while + * processing __VA_ARGS__ to avoid processing empty arguments. + * + * Note that this macro is intended to check macro names that evaluate + * to replacement lists being empty or containing numbers or macro name + * like tokens. + * + * @note Not all arguments are accepted by this macro and compilation will fail + * if argument cannot be concatenated with literal constant. That will + * happen if argument does not start with letter or number. Example + * arguments that will fail during compilation: .arg, (arg), "arg", {arg}. + * + * Example: + * + * #define EMPTY + * #define NON_EMPTY 1 + * #undef UNDEFINED + * IS_EMPTY(EMPTY) + * IS_EMPTY(NON_EMPTY) + * IS_EMPTY(UNDEFINED) + * #if defined(EMPTY) && IS_EMPTY(EMPTY) == true + * some_conditional_code + * #endif + * + * In above examples, the invocations of IS_EMPTY(...) return @p true, + * @p false, and @p true; @p some_conditional_code is included. + * + * @param a macro to check for emptiness + */ +#define IS_EMPTY(a) Z_IS_EMPTY_(a, 1, 0,) + +/** + * @brief Remove empty arguments from list. + * + * During macro expansion, __VA_ARGS__ and other preprocessor + * generated lists may contain empty elements, e.g.: + * + * #define LIST ,a,b,,d, + * + * Using EMPTY to show each empty element, LIST contains: + * + * EMPTY, a, b, EMPTY, d + * + * When processing such lists, e.g. using FOR_EACH(), all empty elements + * will be processed, and may require filtering out. + * To make that process easier, it is enough to invoke LIST_DROP_EMPTY + * which will remove all empty elements. + * + * Example: + * + * LIST_DROP_EMPTY(LIST) + * + * expands to: + * + * a, b, d + * + * @param ... list to be processed + */ +#define LIST_DROP_EMPTY(...) \ + Z_LIST_DROP_FIRST(FOR_EACH(Z_LIST_NO_EMPTIES, (), __VA_ARGS__)) + +/** + * @brief Macro with an empty expansion + * + * This trivial definition is provided for readability when a macro + * should expand to an empty result, which e.g. is sometimes needed to + * silence checkpatch. + * + * Example: + * + * #define LIST_ITEM(n) , item##n + * + * The above would cause checkpatch to complain, but: + * + * #define LIST_ITEM(n) EMPTY, item##n + * + * would not. + */ +#define EMPTY + +/** + * @brief Macro that expands to its argument + * + * This is useful in macros like @c FOR_EACH() when there is no + * transformation required on the list elements. + * + * @param V any value + */ +#define IDENTITY(V) V + +/** + * @brief Get nth argument from argument list. + * + * @param N Argument index to fetch. Counter from 1. + * @param ... Variable list of argments from which one argument is returned. + * + * @return Nth argument. + */ +#define GET_ARG_N(N, ...) Z_GET_ARG_##N(__VA_ARGS__) + +/** + * @brief Strips n first arguments from the argument list. + * + * @param N Number of arguments to discard. + * @param ... Variable list of argments. + * + * @return argument list without N first arguments. + */ +#define GET_ARGS_LESS_N(N, ...) Z_GET_ARGS_LESS_##N(__VA_ARGS__) + +/** + * @brief Like a || b, but does evaluation and + * short-circuiting at C preprocessor time. + * + * This is not the same as the binary @p || operator; in particular, + * @p a should expand to an integer literal 0 or 1. However, @p b + * can be any value. + * + * This can be useful when @p b is an expression that would cause a + * build error when @p a is 1. + */ +#define UTIL_OR(a, b) COND_CODE_1(UTIL_BOOL(a), (a), (b)) + +/** + * @brief Like a && b, but does evaluation and + * short-circuiting at C preprocessor time. + * + * This is not the same as the binary @p &&, however; in particular, + * @p a should expand to an integer literal 0 or 1. However, @p b + * can be any value. + * + * This can be useful when @p b is an expression that would cause a + * build error when @p a is 0. + */ +#define UTIL_AND(a, b) COND_CODE_1(UTIL_BOOL(a), (b), (0)) + +/** + * @brief Generates a sequence of code. + * + * Example: + * + * #define FOO(i, _) MY_PWM ## i , + * { UTIL_LISTIFY(PWM_COUNT, FOO) } + * + * The above two lines expand to: + * + * { MY_PWM0 , MY_PWM1 , } + * + * @param LEN The length of the sequence. Must be an integer literal less + * than 255. + * @param F A macro function that accepts at least two arguments: + * F(i, ...). @p F is called repeatedly in the expansion. + * Its first argument @p i is the index in the sequence, and + * the variable list of arguments passed to UTIL_LISTIFY are passed + * through to @p F. + * + * @note Calling UTIL_LISTIFY with undefined arguments has undefined + * behavior. + */ +#define UTIL_LISTIFY(LEN, F, ...) UTIL_CAT(Z_UTIL_LISTIFY_, LEN)(F, __VA_ARGS__) + +/** + * @brief Call a macro @p F on each provided argument with a given + * separator between each call. + * + * Example: + * + * #define F(x) int a##x + * FOR_EACH(F, (;), 4, 5, 6); + * + * This expands to: + * + * int a4; + * int a5; + * int a6; + * + * @param F Macro to invoke + * @param sep Separator (e.g. comma or semicolon). Must be in parentheses; + * this is required to enable providing a comma as separator. + * @param ... Variable argument list. The macro @p F is invoked as + * F(element) for each element in the list. + */ +#define FOR_EACH(F, sep, ...) \ + Z_FOR_EACH(F, sep, REVERSE_ARGS(__VA_ARGS__)) + +/** + * @brief Like FOR_EACH(), but with a terminator instead of a separator, + * and drops empty elements from the argument list + * + * The @p sep argument to FOR_EACH(F, (sep), a, b) is a + * separator which is placed between calls to @p F, like this: + * + * FOR_EACH(F, (sep), a, b) // F(a) sep F(b) + * // ^^^ no sep here! + * + * By contrast, the @p term argument to FOR_EACH_NONEMPTY_TERM(F, (term), + * a, b) is added after each time @p F appears in the expansion: + * + * FOR_EACH_NONEMPTY_TERM(F, (term), a, b) // F(a) term F(b) term + * // ^^^^ + * + * Further, any empty elements are dropped: + * + * FOR_EACH_NONEMPTY_TERM(F, (term), a, EMPTY, b) // F(a) term F(b) term + * + * This is more convenient in some cases, because FOR_EACH_NONEMPTY_TERM() + * expands to nothing when given an empty argument list, and it's + * often cumbersome to write a macro @p F that does the right thing + * even when given an empty argument. + * + * One example is when __VA_ARGS__ may or may not be empty, + * and the results are embedded in a larger initializer: + * + * #define SQUARE(x) ((x)*(x)) + * + * int my_array[] = { + * FOR_EACH_NONEMPTY_TERM(SQUARE, (,), FOO(...)) + * FOR_EACH_NONEMPTY_TERM(SQUARE, (,), BAR(...)) + * FOR_EACH_NONEMPTY_TERM(SQUARE, (,), BAZ(...)) + * }; + * + * This is more convenient than: + * + * 1. figuring out whether the @p FOO, @p BAR, and @p BAZ expansions + * are empty and adding a comma manually (or not) between FOR_EACH() + * calls + * 2. rewriting SQUARE so it reacts appropriately when "x" is empty + * (which would be necessary if e.g. @p FOO expands to nothing) + * + * @param F Macro to invoke on each nonempty element of the variable + * arguments + * @param term Terminator (e.g. comma or semicolon) placed after each + * invocation of F. Must be in parentheses; this is required + * to enable providing a comma as separator. + * @param ... Variable argument list. The macro @p F is invoked as + * F(element) for each nonempty element in the list. + */ +#define FOR_EACH_NONEMPTY_TERM(F, term, ...) \ + COND_CODE_0( \ + /* are there zero non-empty arguments ? */ \ + NUM_VA_ARGS_LESS_1(LIST_DROP_EMPTY(__VA_ARGS__, _)), \ + /* if so, expand to nothing */ \ + (), \ + /* otherwise, expand to: */ \ + (/* FOR_EACH() on nonempty elements, */ \ + FOR_EACH(F, term, LIST_DROP_EMPTY(__VA_ARGS__)) \ + /* plus a final terminator */ \ + __DEBRACKET term \ + )) + +/** + * @brief Call macro @p F on each provided argument, with the argument's index + * as an additional parameter. + * + * This is like FOR_EACH(), except @p F should be a macro which takes two + * arguments: F(index, variable_arg). + * + * Example: + * + * #define F(idx, x) int a##idx = x + * FOR_EACH_IDX(F, (;), 4, 5, 6); + * + * This expands to: + * + * int a0 = 4; + * int a1 = 5; + * int a2 = 6; + * + * @param F Macro to invoke + * @param sep Separator (e.g. comma or semicolon). Must be in parentheses; + * this is required to enable providing a comma as separator. + * @param ... Variable argument list. The macro @p F is invoked as + * F(index, element) for each element in the list. + */ +#define FOR_EACH_IDX(F, sep, ...) \ + Z_FOR_EACH_IDX(F, sep, REVERSE_ARGS(__VA_ARGS__)) + +/** + * @brief Call macro @p F on each provided argument, with an additional fixed + * argument as a parameter. + * + * This is like FOR_EACH(), except @p F should be a macro which takes two + * arguments: F(variable_arg, fixed_arg). + * + * Example: + * + * static void func(int val, void *dev); + * FOR_EACH_FIXED_ARG(func, (;), dev, 4, 5, 6); + * + * This expands to: + * + * func(4, dev); + * func(5, dev); + * func(6, dev); + * + * @param F Macro to invoke + * @param sep Separator (e.g. comma or semicolon). Must be in parentheses; + * this is required to enable providing a comma as separator. + * @param fixed_arg Fixed argument passed to @p F as the second macro parameter. + * @param ... Variable argument list. The macro @p F is invoked as + * F(element, fixed_arg) for each element in the list. + */ +#define FOR_EACH_FIXED_ARG(F, sep, fixed_arg, ...) \ + Z_FOR_EACH_FIXED_ARG(F, sep, fixed_arg, REVERSE_ARGS(__VA_ARGS__)) + +/** + * @brief Calls macro @p F for each variable argument with an index and fixed + * argument + * + * This is like the combination of FOR_EACH_IDX() with FOR_EACH_FIXED_ARG(). + * + * Example: + * + * #define F(idx, x, fixed_arg) int fixed_arg##idx = x + * FOR_EACH_IDX_FIXED_ARG(F, (;), a, 4, 5, 6); + * + * This expands to: + * + * int a0 = 4; + * int a1 = 5; + * int a2 = 6; + * + * @param F Macro to invoke + * @param sep Separator (e.g. comma or semicolon). Must be in parentheses; + * This is required to enable providing a comma as separator. + * @param fixed_arg Fixed argument passed to @p F as the third macro parameter. + * @param ... Variable list of arguments. The macro @p F is invoked as + * F(index, element, fixed_arg) for each element in + * the list. + */ +#define FOR_EACH_IDX_FIXED_ARG(F, sep, fixed_arg, ...) \ + Z_FOR_EACH_IDX_FIXED_ARG(F, sep, fixed_arg, REVERSE_ARGS(__VA_ARGS__)) + +/** @brief Reverse arguments order. + * + * @param ... Variable argument list. + */ +#define REVERSE_ARGS(...) \ + Z_FOR_EACH_ENGINE(Z_FOR_EACH_EXEC, (,), Z_BYPASS, _, __VA_ARGS__) + +/** + * @brief Number of arguments in the variable arguments list minus one. + * + * @param ... List of arguments + * @return Number of variadic arguments in the argument list, minus one + */ +#define NUM_VA_ARGS_LESS_1(...) \ + NUM_VA_ARGS_LESS_1_IMPL(__VA_ARGS__, 63, 62, 61, \ + 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, \ + 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, \ + 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, \ + 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \ + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \ + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, ~) + +/** + * @brief Mapping macro that pastes results together + * + * This is similar to FOR_EACH() in that it invokes a macro repeatedly + * on each element of __VA_ARGS__. However, unlike FOR_EACH(), + * MACRO_MAP_CAT() pastes the results together into a single token. + * + * For example, with this macro FOO: + * + * #define FOO(x) item_##x##_ + * + * MACRO_MAP_CAT(FOO, a, b, c), expands to the token: + * + * item_a_item_b_item_c_ + * + * @param ... Macro to expand on each argument, followed by its + * arguments. (The macro should take exactly one argument.) + * @return The results of expanding the macro on each argument, all pasted + * together + */ +#define MACRO_MAP_CAT(...) MACRO_MAP_CAT_(__VA_ARGS__) + +/** + * @brief Mapping macro that pastes a fixed number of results together + * + * Similar to @ref MACRO_MAP_CAT(), but expects a fixed number of + * arguments. If more arguments are given than are expected, the rest + * are ignored. + * + * @param N Number of arguments to map + * @param ... Macro to expand on each argument, followed by its + * arguments. (The macro should take exactly one argument.) + * @return The results of expanding the macro on each argument, all pasted + * together + */ +#define MACRO_MAP_CAT_N(N, ...) MACRO_MAP_CAT_N_(N, __VA_ARGS__) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_SYS_UTIL_MACROS_H_ */ diff --git a/framework/include/fwk_devicetree.h b/framework/include/fwk_devicetree.h new file mode 100644 index 0000000000000000000000000000000000000000..a922dc54bf6decec429374ed0a142d279537287c --- /dev/null +++ b/framework/include/fwk_devicetree.h @@ -0,0 +1,2655 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2020 Nordic Semiconductor + * Copyright (c) 2020, Linaro Ltd. + * + * Copy of the devicetree.h file from Zephyr - with zephyr specific elements removed. + */ +#ifndef FWK_DEVICETREE_H +#define FWK_DEVICETREE_H + +#include + +/** + * @brief devicetree.h API + * @defgroup devicetree Devicetree + * @{ + * @} + */ + +/* + * Property suffixes + * ----------------- + * + * These are the optional parts that come after the _P_ + * part in DT_N__P_ macros, or the "prop-suf" + * nonterminal in the DT guide's macros.bnf file. + * + * Before adding new ones, check this list to avoid conflicts. If any + * are missing from this list, please add them. It should be complete. + * + * _ENUM_IDX: property's value as an index into bindings enum + * _ENUM_TOKEN: property's value as a token into bindings enum (string + * enum values are identifiers) [deprecated, use _STRING_TOKEN] + * _ENUM_UPPER_TOKEN: like _ENUM_TOKEN, but uppercased [deprecated, use + * _STRING_UPPER_TOKEN] + * _EXISTS: property is defined + * _FOREACH_PROP_ELEM: helper for "iterating" over values in the property + * _FOREACH_PROP_ELEM_VARGS: foreach functions with variable number of arguments + * _IDX_: logical index into property + * _IDX__EXISTS: logical index into property is defined + * _IDX__PH: phandle array's phandle by index (or phandle, phandles) + * _IDX__VAL_: phandle array's specifier value by index + * _IDX__VAL__EXISTS: cell value exists, by index + * _LEN: property logical length + * _NAME__PH: phandle array's phandle by name + * _NAME__VAL_: phandle array's property specifier by name + * _NAME__VAL__EXISTS: cell value exists, by name + * _STRING_TOKEN: string property's value as a token + * _STRING_UPPER_TOKEN: like _STRING_TOKEN, but uppercased + */ + +/** + * @defgroup devicetree-generic-id Node identifiers and helpers + * @ingroup devicetree + * @{ + */ + +/** + * @brief Name for an invalid node identifier + * + * This supports cases where factored macros can be invoked from paths where + * devicetree data may or may not be available. It is a preprocessor identifier + * that does not match any valid devicetree node identifier. + */ +#define DT_INVALID_NODE _ + +/** + * @brief Node identifier for the root node in the devicetree + */ +#define DT_ROOT DT_N + +/** + * @brief Get a node identifier for a devicetree path + * + * (This macro returns a node identifier from path components. To get + * a path string from a node identifier, use DT_NODE_PATH() instead.) + * + * The arguments to this macro are the names of non-root nodes in the + * tree required to reach the desired node, starting from the root. + * Non-alphanumeric characters in each name must be converted to + * underscores to form valid C tokens, and letters must be lowercased. + * + * Example devicetree fragment: + * + * / { + * soc { + * serial1: serial@40001000 { + * status = "okay"; + * current-speed = <115200>; + * ... + * }; + * }; + * }; + * + * You can use DT_PATH(soc, serial_40001000) to get a node identifier + * for the serial@40001000 node. Node labels like "serial1" cannot be + * used as DT_PATH() arguments; use DT_NODELABEL() for those instead. + * + * Example usage with DT_PROP() to get the current-speed property: + * + * DT_PROP(DT_PATH(soc, serial_40001000), current_speed) // 115200 + * + * (The current-speed property is also in "lowercase-and-underscores" + * form when used with this API.) + * + * When determining arguments to DT_PATH(): + * + * - the first argument corresponds to a child node of the root ("soc" above) + * - a second argument corresponds to a child of the first argument + * ("serial_40001000" above, from the node name "serial@40001000" + * after lowercasing and changing "@" to "_") + * - and so on for deeper nodes in the desired node's path + * + * @param ... lowercase-and-underscores node names along the node's path, + * with each name given as a separate argument + * @return node identifier for the node with that path + */ +#define DT_PATH(...) DT_PATH_INTERNAL(__VA_ARGS__) + +/** + * @brief Get a node identifier for a node label + * + * Convert non-alphanumeric characters in the node label to + * underscores to form valid C tokens, and lowercase all letters. Note + * that node labels are not the same thing as label properties. + * + * Example devicetree fragment: + * + * serial1: serial@40001000 { + * label = "UART_0"; + * status = "okay"; + * current-speed = <115200>; + * ... + * }; + * + * The only node label in this example is "serial1". + * + * The string "UART_0" is *not* a node label; it's the value of a + * property named label. + * + * You can use DT_NODELABEL(serial1) to get a node identifier for the + * serial@40001000 node. Example usage with DT_PROP() to get the + * current-speed property: + * + * DT_PROP(DT_NODELABEL(serial1), current_speed) // 115200 + * + * Another example devicetree fragment: + * + * cpu@0 { + * L2_0: l2-cache { + * cache-level = <2>; + * ... + * }; + * }; + * + * Example usage to get the cache-level property: + * + * DT_PROP(DT_NODELABEL(l2_0), cache_level) // 2 + * + * Notice how "L2_0" in the devicetree is lowercased to "l2_0" in the + * DT_NODELABEL() argument. + * + * @param label lowercase-and-underscores node label name + * @return node identifier for the node with that label + */ +#define DT_NODELABEL(label) DT_CAT(DT_N_NODELABEL_, label) + +/** + * @brief Get a node identifier from /aliases + * + * This macro's argument is a property of the /aliases node. It + * returns a node identifier for the node which is aliased. Convert + * non-alphanumeric characters in the alias property to underscores to + * form valid C tokens, and lowercase all letters. + * + * Example devicetree fragment: + * + * / { + * aliases { + * my-serial = &serial1; + * }; + * + * soc { + * serial1: serial@40001000 { + * status = "okay"; + * current-speed = <115200>; + * ... + * }; + * }; + * }; + * + * You can use DT_ALIAS(my_serial) to get a node identifier for the + * serial@40001000 node. Notice how my-serial in the devicetree + * becomes my_serial in the DT_ALIAS() argument. Example usage with + * DT_PROP() to get the current-speed property: + * + * DT_PROP(DT_ALIAS(my_serial), current_speed) // 115200 + * + * @param alias lowercase-and-underscores alias name. + * @return node identifier for the node with that alias + */ +#define DT_ALIAS(alias) DT_CAT(DT_N_ALIAS_, alias) + +/** + * @brief Get a node identifier for an instance of a compatible + * + * All nodes with a particular compatible property value are assigned + * instance numbers, which are zero-based indexes specific to that + * compatible. You can get a node identifier for these nodes by + * passing DT_INST() an instance number, "inst", along with the + * lowercase-and-underscores version of the compatible, "compat". + * + * Instance numbers have the following properties: + * + * - for each compatible, instance numbers start at 0 and are contiguous + * - exactly one instance number is assigned for each node with a compatible, + * **including disabled nodes** + * - enabled nodes (status property is "okay" or missing) are assigned the + * instance numbers starting from 0, and disabled nodes have instance + * numbers which are greater than those of any enabled node + * + * No other guarantees are made. In particular: + * + * - instance numbers **in no way reflect** any numbering scheme that + * might exist in SoC documentation, node labels or unit addresses, + * or properties of the /aliases node (use DT_NODELABEL() or DT_ALIAS() + * for those) + * - there **is no general guarantee** that the same node will have + * the same instance number between builds, even if you are building + * the same application again in the same build directory + * + * Example devicetree fragment: + * + * serial1: serial@40001000 { + * compatible = "vnd,soc-serial"; + * status = "disabled"; + * current-speed = <9600>; + * ... + * }; + * + * serial2: serial@40002000 { + * compatible = "vnd,soc-serial"; + * status = "okay"; + * current-speed = <57600>; + * ... + * }; + * + * serial3: serial@40003000 { + * compatible = "vnd,soc-serial"; + * current-speed = <115200>; + * ... + * }; + * + * Assuming no other nodes in the devicetree have compatible + * "vnd,soc-serial", that compatible has nodes with instance numbers + * 0, 1, and 2. + * + * The nodes serial@40002000 and serial@40003000 are both enabled, so + * their instance numbers are 0 and 1, but no guarantees are made + * regarding which node has which instance number. + * + * Since serial@40001000 is the only disabled node, it has instance + * number 2, since disabled nodes are assigned the largest instance + * numbers. Therefore: + * + * // Could be 57600 or 115200. There is no way to be sure: + * // either serial@40002000 or serial@40003000 could + * // have instance number 0, so this could be the current-speed + * // property of either of those nodes. + * DT_PROP(DT_INST(0, vnd_soc_serial), current_speed) + * + * // Could be 57600 or 115200, for the same reason. + * // If the above expression expands to 57600, then + * // this expands to 115200, and vice-versa. + * DT_PROP(DT_INST(1, vnd_soc_serial), current_speed) + * + * // 9600, because there is only one disabled node, and + * // disabled nodes are "at the the end" of the instance + * // number "list". + * DT_PROP(DT_INST(2, vnd_soc_serial), current_speed) + * + * Notice how "vnd,soc-serial" in the devicetree becomes vnd_soc_serial + * (without quotes) in the DT_INST() arguments. (As usual, current-speed + * in the devicetree becomes current_speed as well.) + * + * Nodes whose "compatible" property has multiple values are assigned + * independent instance numbers for each compatible. + * + * @param inst instance number for compatible "compat" + * @param compat lowercase-and-underscores compatible, without quotes + * @return node identifier for the node with that instance number and + * compatible + */ +#define DT_INST(inst, compat) UTIL_CAT(DT_N_INST, DT_DASH(inst, compat)) + +/** + * @brief Get a node identifier for a parent node + * + * Example devicetree fragment: + * + * parent: parent-node { + * child: child-node { + * ... + * }; + * }; + * + * The following are equivalent ways to get the same node identifier: + * + * DT_NODELABEL(parent) + * DT_PARENT(DT_NODELABEL(child)) + * + * @param node_id node identifier + * @return a node identifier for the node's parent + */ +#define DT_PARENT(node_id) UTIL_CAT(node_id, _PARENT) + +/** + * @brief Get a node identifier for a grandparent node + * + * Example devicetree fragment: + * + * gparent: grandparent-node { + * parent: parent-node { + * child: child-node { ... } + * }; + * }; + * + * The following are equivalent ways to get the same node identifier: + * + * DT_GPARENT(DT_NODELABEL(child)) + * DT_PARENT(DT_PARENT(DT_NODELABEL(child)) + * + * @param node_id node identifier + * @return a node identifier for the node's parent's parent + */ +#define DT_GPARENT(node_id) DT_PARENT(DT_PARENT(node_id)) + +/** + * @brief Get a node identifier for a child node + * + * Example devicetree fragment: + * + * / { + * soc-label: soc { + * serial1: serial@40001000 { + * status = "okay"; + * current-speed = <115200>; + * ... + * }; + * }; + * }; + * + * Example usage with @ref DT_PROP() to get the status of the + * serial@40001000 node: + * + * #define SOC_NODE DT_NODELABEL(soc_label) + * DT_PROP(DT_CHILD(SOC_NODE, serial_40001000), status) // "okay" + * + * Node labels like "serial1" cannot be used as the "child" argument + * to this macro. Use DT_NODELABEL() for that instead. + * + * You can also use DT_FOREACH_CHILD() to iterate over node + * identifiers for all of a node's children. + * + * @param node_id node identifier + * @param child lowercase-and-underscores child node name + * @return node identifier for the node with the name referred to by 'child' + */ +#define DT_CHILD(node_id, child) UTIL_CAT(node_id, DT_S_PREFIX(child)) + +/** + * @brief Get a node identifier for a status "okay" node with a compatible + * + * Use this if you want to get an arbitrary enabled node with a given + * compatible, and you do not care which one you get. If any enabled + * nodes with the given compatible exist, a node identifier for one + * of them is returned. Otherwise, @p DT_INVALID_NODE is returned. + * + * Example devicetree fragment: + * + * node-a { + * compatible = "vnd,device"; + * status = "okay"; + * }; + * + * node-b { + * compatible = "vnd,device"; + * status = "okay"; + * }; + * + * node-c { + * compatible = "vnd,device"; + * status = "disabled"; + * }; + * + * Example usage: + * + * DT_COMPAT_GET_ANY_STATUS_OKAY(vnd_device) + * + * This expands to a node identifier for either @p node-a or @p + * node-b. It will not expand to a node identifier for @p node-c, + * because that node does not have status "okay". + * + * @param compat lowercase-and-underscores compatible, without quotes + * @return node identifier for a node with that compatible, or DT_INVALID_NODE + */ +#define DT_COMPAT_GET_ANY_STATUS_OKAY(compat) \ + COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(compat), \ + (DT_INST(0, compat)), \ + (DT_INVALID_NODE)) + +/** + * @brief Get a devicetree node's full path as a string literal + * + * This returns the path to a node from a node identifier. To get a + * node identifier from path components instead, use DT_PATH(). + * + * Example devicetree fragment: + * + * / { + * soc { + * node: my-node@12345678 { ... }; + * }; + * }; + * + * Example usage: + * + * DT_NODE_PATH(DT_NODELABEL(node)) // "/soc/my-node@12345678" + * DT_NODE_PATH(DT_PATH(soc)) // "/soc" + * DT_NODE_PATH(DT_ROOT) // "/" + * + * @param node_id node identifier + * @return the node's full path in the devicetree + */ +#define DT_NODE_PATH(node_id) DT_CAT(node_id, _PATH) + +/** + * @brief Get a devicetree node's name with unit-address as a string literal + * + * This returns the node name and unit-address from a node identifier. + * + * Example devicetree fragment: + * + * / { + * soc { + * node: my-node@12345678 { ... }; + * }; + * }; + * + * Example usage: + * + * DT_NODE_FULL_NAME(DT_NODELABEL(node)) // "my-node@12345678" + * + * @param node_id node identifier + * @return the node's name with unit-address as a string in the devicetree + */ +#define DT_NODE_FULL_NAME(node_id) DT_CAT(node_id, _FULL_NAME) + +/** + * @brief Do node_id1 and node_id2 refer to the same node? + * + * Both "node_id1" and "node_id2" must be node identifiers for nodes + * that exist in the devicetree (if unsure, you can check with + * DT_NODE_EXISTS()). + * + * The expansion evaluates to 0 or 1, but may not be a literal integer + * 0 or 1. + * + * @param node_id1 first node identifer + * @param node_id2 second node identifier + * @return an expression that evaluates to 1 if the node identifiers + * refer to the same node, and evaluates to 0 otherwise + */ +#define DT_SAME_NODE(node_id1, node_id2) \ + (DT_DEP_ORD(node_id1) == (DT_DEP_ORD(node_id2))) + +/* Implementation note: distinct nodes have distinct node identifiers. + * See include/devicetree/ordinals.h. */ + +/** + * @} + */ + +/** + * @defgroup devicetree-generic-prop Property accessors + * @ingroup devicetree + * @{ + */ + +/** + * @brief Get a devicetree property value + * + * For properties whose bindings have the following types, this macro + * expands to: + * + * - string: a string literal + * - boolean: 0 if the property is false, or 1 if it is true + * - int: the property's value as an integer literal + * - array, uint8-array, string-array: an initializer expression in braces, + * whose elements are integer or string literals (like {0, 1, 2}, + * {"hello", "world"}, etc.) + * - phandle: a node identifier for the node with that phandle + * + * A property's type is usually defined by its binding. In some + * special cases, it has an assumed type defined by the devicetree + * specification even when no binding is available: "compatible" has + * type string-array, "status" and "label" have type string, and + * "interrupt-controller" has type boolean. + * + * For other properties or properties with unknown type due to a + * missing binding, behavior is undefined. + * + * For usage examples, see @ref DT_PATH(), @ref DT_ALIAS(), @ref + * DT_NODELABEL(), and @ref DT_INST() above. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name + * @return a representation of the property's value + */ +#define DT_PROP(node_id, prop) DT_CAT(node_id, _P_##prop) + +/** + * @brief Get a property's logical length + * + * Here, "length" is a number of elements, which may differ from the + * property's size in bytes. + * + * The return value depends on the property's type: + * + * - for types array, string-array, and uint8-array, this expands + * to the number of elements in the array + * - for type phandles, this expands to the number of phandles + * - for type phandle-array, this expands to the number of + * phandle and specifier blocks in the property + * + * These properties are handled as special cases: + * + * - reg property: use DT_NUM_REGS(node_id) instead + * - interrupts property: use DT_NUM_IRQS(node_id) instead + * + * It is an error to use this macro with the reg or interrupts properties. + * + * For other properties, behavior is undefined. + * + * @param node_id node identifier + * @param prop a lowercase-and-underscores property with a logical length + * @return the property's length + */ +#define DT_PROP_LEN(node_id, prop) DT_PROP(node_id, prop##_LEN) + +/** + * @brief Like DT_PROP_LEN(), but with a fallback to default_value + * + * If the property is defined (as determined by DT_NODE_HAS_PROP()), + * this expands to DT_PROP_LEN(node_id, prop). The default_value + * parameter is not expanded in this case. + * + * Otherwise, this expands to default_value. + * + * @param node_id node identifier + * @param prop a lowercase-and-underscores property with a logical length + * @param default_value a fallback value to expand to + * @return the property's length or the given default value + */ +#define DT_PROP_LEN_OR(node_id, prop, default_value) \ + COND_CODE_1(DT_NODE_HAS_PROP(node_id, prop), \ + (DT_PROP_LEN(node_id, prop)), (default_value)) + +/** + * @brief Is index "idx" valid for an array type property? + * + * If this returns 1, then DT_PROP_BY_IDX(node_id, prop, idx) or + * DT_PHA_BY_IDX(node_id, prop, idx, ...) are valid at index "idx". + * If it returns 0, it is an error to use those macros with that index. + * + * These properties are handled as special cases: + * + * - reg property: use DT_REG_HAS_IDX(node_id, idx) instead + * - interrupts property: use DT_IRQ_HAS_IDX(node_id, idx) instead + * + * It is an error to use this macro with the reg or interrupts properties. + * + * @param node_id node identifier + * @param prop a lowercase-and-underscores property with a logical length + * @param idx index to check + * @return An expression which evaluates to 1 if "idx" is a valid index + * into the given property, and 0 otherwise. + */ +#define DT_PROP_HAS_IDX(node_id, prop, idx) \ + IS_ENABLED(DT_CAT6(node_id, _P_, prop, _IDX_, idx, _EXISTS)) + +/** + * @brief Get the value at index "idx" in an array type property + * + * It might help to read the argument order as being similar to + * "node->property[index]". + * + * When the property's binding has type array, string-array, + * uint8-array, or phandles, this expands to the idx-th array element + * as an integer, string literal, or node identifier respectively. + * + * These properties are handled as special cases: + * + * - reg property: use DT_REG_ADDR_BY_IDX() or DT_REG_SIZE_BY_IDX() instead + * - interrupts property: use DT_IRQ_BY_IDX() instead + * + * For non-array properties, behavior is undefined. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name + * @param idx the index to get + * @return a representation of the idx-th element of the property + */ +#define DT_PROP_BY_IDX(node_id, prop, idx) DT_PROP(node_id, prop##_IDX_##idx) + +/** + * @brief Like DT_PROP(), but with a fallback to default_value + * + * If the value exists, this expands to DT_PROP(node_id, prop). + * The default_value parameter is not expanded in this case. + * + * Otherwise, this expands to default_value. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name + * @param default_value a fallback value to expand to + * @return the property's value or default_value + */ +#define DT_PROP_OR(node_id, prop, default_value) \ + COND_CODE_1(DT_NODE_HAS_PROP(node_id, prop), \ + (DT_PROP(node_id, prop)), (default_value)) + +/** + * @brief Equivalent to DT_PROP(node_id, label) + * + * This is a convenience for the Zephyr device API, which uses label + * properties as device_get_binding() arguments. + * @param node_id node identifier + * @return node's label property value + */ +#define DT_LABEL(node_id) DT_PROP(node_id, label) + +/** + * @brief Get a property value's index into its enumeration values + * + * The return values start at zero. + * + * Example devicetree fragment: + * + * usb1: usb@12340000 { + * maximum-speed = "full-speed"; + * }; + * usb2: usb@12341000 { + * maximum-speed = "super-speed"; + * }; + * + * Example bindings fragment: + * + * properties: + * maximum-speed: + * type: string + * enum: + * - "low-speed" + * - "full-speed" + * - "high-speed" + * - "super-speed" + * + * Example usage: + * + * DT_ENUM_IDX(DT_NODELABEL(usb1), maximum_speed) // 1 + * DT_ENUM_IDX(DT_NODELABEL(usb2), maximum_speed) // 3 + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name + * @return zero-based index of the property's value in its enum: list + */ +#define DT_ENUM_IDX(node_id, prop) DT_PROP(node_id, prop##_ENUM_IDX) + +/** + * @brief Like DT_ENUM_IDX(), but with a fallback to a default enum index + * + * If the value exists, this expands to its zero based index value thanks to + * DT_ENUM_IDX(node_id, prop). + * + * Otherwise, this expands to provided default index enum value. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name + * @param default_idx_value a fallback index value to expand to + * @return zero-based index of the property's value in its enum if present, + * default_idx_value ohterwise + */ +#define DT_ENUM_IDX_OR(node_id, prop, default_idx_value) \ + COND_CODE_1(DT_NODE_HAS_PROP(node_id, prop), \ + (DT_ENUM_IDX(node_id, prop)), (default_idx_value)) + +/** + * @brief Get a string property's value as a token. + * + * This removes "the quotes" from string-valued properties, and converts + * non-alphanumeric characters to underscores. That can be useful, for example, + * when programmatically using the value to form a C variable or code. + * + * DT_STRING_TOKEN() can only be used for properties with string type. + * + * It is an error to use DT_STRING_TOKEN() in other circumstances. + * + * Example devicetree fragment: + * + * n1: node-1 { + * prop = "foo"; + * }; + * n2: node-2 { + * prop = "FOO"; + * } + * n3: node-3 { + * prop = "123 foo"; + * }; + * + * Example bindings fragment: + * + * properties: + * prop: + * type: string + * + * Example usage: + * + * DT_STRING_TOKEN(DT_NODELABEL(n1), prop) // foo + * DT_STRING_TOKEN(DT_NODELABEL(n2), prop) // FOO + * DT_STRING_TOKEN(DT_NODELABEL(n3), prop) // 123_foo + * + * Notice how: + * + * - Unlike C identifiers, the property values may begin with a + * number. It's the user's responsibility not to use such values as + * the name of a C identifier. + * + * - The uppercased "FOO" in the DTS remains @p FOO as a token. It is + * *not* converted to @p foo. + * + * - The whitespace in the DTS "123 foo" string is converted to @p + * 123_foo as a token. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property string name + * @return the value of @p prop as a token, i.e. without any quotes + * and with special characters converted to underscores + */ +#define DT_STRING_TOKEN(node_id, prop) \ + DT_CAT4(node_id, _P_, prop, _STRING_TOKEN) + +/** + * @brief Like DT_STRING_TOKEN(), but uppercased. + * + * This removes "the quotes and capitalize" from string-valued properties, and + * converts non-alphanumeric characters to underscores. That can be useful, for + * example, when programmatically using the value to form a C variable or code. + * + * DT_STRING_UPPER_TOKEN() can only be used for properties with string type. + * + * It is an error to use DT_STRING_UPPER_TOKEN() in other circumstances. + * + * Example devicetree fragment: + * + * n1: node-1 { + * prop = "foo"; + * }; + * n2: node-2 { + * prop = "123 foo"; + * }; + * + * Example bindings fragment: + * + * properties: + * prop: + * type: string + * + * Example usage: + * + * DT_STRING_UPPER_TOKEN(DT_NODELABEL(n1), prop) // FOO + * DT_STRING_UPPER_TOKEN(DT_NODELABEL(n2), prop) // 123_FOO + * + * Notice how: + * + * - Unlike C identifiers, the property values may begin with a + * number. It's the user's responsibility not to use such values as + * the name of a C identifier. + * + * - The lowercased "foo" in the DTS becomes @p FOO as a token, i.e. + * it is uppercased. + * + * - The whitespace in the DTS "123 foo" string is converted to @p + * 123_FOO as a token, i.e. it is uppercased and whitespace becomes + * an underscore. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property string name + * @return the value of @p prop as a token, i.e. without any quotes + * and with special characters converted to underscores + */ +#define DT_STRING_UPPER_TOKEN(node_id, prop) \ + DT_CAT4(node_id, _P_, prop, _STRING_UPPER_TOKEN) + +/** + * @brief Get an enumeration property's value as a token. + * + * This allows you to "remove the quotes" from some string-valued + * properties. That can be useful, for example, when pasting the + * values onto some other token to form an enum in C using the @p ## + * preprocessor operator. + * + * DT_ENUM_TOKEN() can only be used for properties with string type + * whose binding has an "enum:". The values in the binding's "enum:" + * list must be unique after converting non-alphanumeric characters to + * underscores. + * + * It is an error to use DT_ENUM_TOKEN() in other circumstances. + * + * Example devicetree fragment: + * + * n1: node-1 { + * prop = "foo"; + * }; + * n2: node-2 { + * prop = "FOO"; + * } + * n3: node-3 { + * prop = "123 foo"; + * }; + * + * Example bindings fragment: + * + * properties: + * prop: + * type: string + * enum: + * - "foo" + * - "FOO" + * - "123 foo" + * + * Example usage: + * + * DT_ENUM_TOKEN(DT_NODELABEL(n1), prop) // foo + * DT_ENUM_TOKEN(DT_NODELABEL(n2), prop) // FOO + * DT_ENUM_TOKEN(DT_NODELABEL(n3), prop) // 123_foo + * + * Notice how: + * + * - Unlike C identifiers, the property values may begin with a + * number. It's the user's responsibility not to use such values as + * the name of a C identifier. + * + * - The uppercased "FOO" in the DTS remains @p FOO as a token. It is + *not* converted to @p foo. + * + * - The whitespace in the DTS "123 foo" string is converted to @p + * 123_foo as a token. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name with suitable + * enumeration of values in its binding + * @return the value of @p prop as a token, i.e. without any quotes + * and with special characters converted to underscores + */ +#define DT_ENUM_TOKEN(node_id, prop) \ + __DEPRECATED_MACRO \ + DT_CAT4(node_id, _P_, prop, _ENUM_TOKEN) + +/** + * @brief Like DT_ENUM_TOKEN(), but uppercased + * + * This allows you to "remove the quotes and capitalize" some string-valued + * properties. + * + * DT_ENUM_UPPER_TOKEN() can only be used for properties with string type + * whose binding has an "enum:". The values in the binding's "enum:" + * list must be unique after converting non-alphanumeric characters to + * underscores and capitalizating any letters. + * + * It is an error to use DT_ENUM_UPPER_TOKEN() in other circumstances. + * + * Example devicetree fragment: + * + * n1: node-1 { + * prop = "foo"; + * }; + * n2: node-2 { + * prop = "123 foo"; + * }; + * + * Example bindings fragment: + * + * properties: + * prop: + * type: string + * enum: + * - "foo" + * - "123 foo" + * + * Example usage: + * + * DT_ENUM_TOKEN((DT_NODELABEL(n1), prop) // FOO + * DT_ENUM_TOKEN((DT_NODELABEL(n2), prop) // 123_FOO + * + * Notice how: + * + * - Unlike C identifiers, the property values may begin with a + * number. It's the user's responsibility not to use such values as + * the name of a C identifier. + * + * - The lowercased "foo" in the DTS becomes @p FOO as a token, i.e. + * it is uppercased. + * + * - The whitespace in the DTS "123 foo" string is converted to @p + * 123_FOO as a token, i.e. it is uppercased and whitespace becomes + * an underscore. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name with suitable + * enumeration of values in its binding + * @return the value of @p prop as a capitalized token, i.e. upper case, + * without any quotes, and with special characters converted to + * underscores + */ +#define DT_ENUM_UPPER_TOKEN(node_id, prop) \ + __DEPRECATED_MACRO \ + DT_CAT4(node_id, _P_, prop, _ENUM_UPPER_TOKEN) + +/* + * phandle properties + * + * These are special-cased to manage the impedance mismatch between + * phandles, which are just uint32_t node properties that only make sense + * within the tree itself, and C values. + */ + +/** + * @brief Get a property value from a phandle in a property. + * + * This is a shorthand for: + * + * DT_PROP(DT_PHANDLE_BY_IDX(node_id, phs, idx), prop) + * + * That is, "prop" is a property of the phandle's node, not a + * property of "node_id". + * + * Example devicetree fragment: + * + * n1: node-1 { + * foo = <&n2 &n3>; + * }; + * + * n2: node-2 { + * bar = <42>; + * }; + * + * n3: node-3 { + * baz = <43>; + * }; + * + * Example usage: + * + * #define N1 DT_NODELABEL(n1) + * + * DT_PROP_BY_PHANDLE_IDX(N1, foo, 0, bar) // 42 + * DT_PROP_BY_PHANDLE_IDX(N1, foo, 1, baz) // 43 + * + * @param node_id node identifier + * @param phs lowercase-and-underscores property with type "phandle", + * "phandles", or "phandle-array" + * @param idx logical index into "phs", which must be zero if "phs" + * has type "phandle" + * @param prop lowercase-and-underscores property of the phandle's node + * @return the property's value + */ +#define DT_PROP_BY_PHANDLE_IDX(node_id, phs, idx, prop) \ + DT_PROP(DT_PHANDLE_BY_IDX(node_id, phs, idx), prop) + +/** + * @brief Like DT_PROP_BY_PHANDLE_IDX(), but with a fallback to + * default_value. + * + * If the value exists, this expands to DT_PROP_BY_PHANDLE_IDX(node_id, phs, + * idx, prop). The default_value parameter is not expanded in this + * case. + * + * Otherwise, this expands to default_value. + * + * @param node_id node identifier + * @param phs lowercase-and-underscores property with type "phandle", + * "phandles", or "phandle-array" + * @param idx logical index into "phs", which must be zero if "phs" + * has type "phandle" + * @param prop lowercase-and-underscores property of the phandle's node + * @param default_value a fallback value to expand to + * @return the property's value + */ +#define DT_PROP_BY_PHANDLE_IDX_OR(node_id, phs, idx, prop, default_value) \ + DT_PROP_OR(DT_PHANDLE_BY_IDX(node_id, phs, idx), prop, default_value) + +/** + * @brief Get a property value from a phandle's node + * + * This is equivalent to DT_PROP_BY_PHANDLE_IDX(node_id, ph, 0, prop). + * + * @param node_id node identifier + * @param ph lowercase-and-underscores property of "node_id" + * with type "phandle" + * @param prop lowercase-and-underscores property of the phandle's node + * @return the property's value + */ +#define DT_PROP_BY_PHANDLE(node_id, ph, prop) \ + DT_PROP_BY_PHANDLE_IDX(node_id, ph, 0, prop) + +/** + * @brief Get a phandle-array specifier cell value at an index + * + * It might help to read the argument order as being similar to + * "node->phandle_array[index].cell". That is, the cell value is in + * the "pha" property of "node_id", inside the specifier at index + * "idx". + * + * Example devicetree fragment: + * + * gpio0: gpio@... { + * #gpio-cells = <2>; + * }; + * + * gpio1: gpio@... { + * #gpio-cells = <2>; + * }; + * + * led: led_0 { + * gpios = <&gpio0 17 0x1>, <&gpio1 5 0x3>; + * }; + * + * Bindings fragment for the gpio0 and gpio1 nodes: + * + * gpio-cells: + * - pin + * - flags + * + * Above, "gpios" has two elements: + * + * - index 0 has specifier <17 0x1>, so its "pin" cell is 17, and its + * "flags" cell is 0x1 + * - index 1 has specifier <5 0x3>, so "pin" is 5 and "flags" is 0x3 + * + * Example usage: + * + * #define LED DT_NODELABEL(led) + * + * DT_PHA_BY_IDX(LED, gpios, 0, pin) // 17 + * DT_PHA_BY_IDX(LED, gpios, 1, flags) // 0x3 + * + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param idx logical index into "pha" + * @param cell lowercase-and-underscores cell name within the specifier + * at "pha" index "idx" + * @return the cell's value + */ +#define DT_PHA_BY_IDX(node_id, pha, idx, cell) \ + DT_PROP(node_id, pha##_IDX_##idx##_VAL_##cell) + +/** + * @brief Like DT_PHA_BY_IDX(), but with a fallback to default_value. + * + * If the value exists, this expands to DT_PHA_BY_IDX(node_id, pha, + * idx, cell). The default_value parameter is not expanded in this + * case. + * + * Otherwise, this expands to default_value. + * + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param idx logical index into "pha" + * @param cell lowercase-and-underscores cell name within the specifier + * at "pha" index "idx" + * @param default_value a fallback value to expand to + * @return the cell's value or "default_value" + */ +#define DT_PHA_BY_IDX_OR(node_id, pha, idx, cell, default_value) \ + DT_PROP_OR(node_id, pha##_IDX_##idx##_VAL_##cell, default_value) +/* Implementation note: the _IDX_##idx##_VAL_##cell##_EXISTS + * macros are defined, so it's safe to use DT_PROP_OR() here, because + * that uses an IS_ENABLED() on the _EXISTS macro. + */ + +/** + * @brief Equivalent to DT_PHA_BY_IDX(node_id, pha, 0, cell) + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param cell lowercase-and-underscores cell name + * @return the cell's value + */ +#define DT_PHA(node_id, pha, cell) DT_PHA_BY_IDX(node_id, pha, 0, cell) + +/** + * @brief Like DT_PHA(), but with a fallback to default_value + * + * If the value exists, this expands to DT_PHA(node_id, pha, cell). + * The default_value parameter is not expanded in this case. + * + * Otherwise, this expands to default_value. + * + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param cell lowercase-and-underscores cell name + * @param default_value a fallback value to expand to + * @return the cell's value or default_value + */ +#define DT_PHA_OR(node_id, pha, cell, default_value) \ + DT_PHA_BY_IDX_OR(node_id, pha, 0, cell, default_value) + +/** + * @brief Get a value within a phandle-array specifier by name + * + * This is like DT_PHA_BY_IDX(), except it treats "pha" as a structure + * where each array element has a name. + * + * It might help to read the argument order as being similar to + * "node->phandle_struct.name.cell". That is, the cell value is in the + * "pha" property of "node_id", treated as a data structure where + * each array element has a name. + * + * Example devicetree fragment: + * + * n: node { + * io-channels = <&adc1 10>, <&adc2 20>; + * io-channel-names = "SENSOR", "BANDGAP"; + * }; + * + * Bindings fragment for the "adc1" and "adc2" nodes: + * + * io-channel-cells: + * - input + * + * Example usage: + * + * DT_PHA_BY_NAME(DT_NODELABEL(n), io_channels, sensor, input) // 10 + * DT_PHA_BY_NAME(DT_NODELABEL(n), io_channels, bandgap, input) // 20 + * + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param name lowercase-and-underscores name of a specifier in "pha" + * @param cell lowercase-and-underscores cell name in the named specifier + * @return the cell's value + */ +#define DT_PHA_BY_NAME(node_id, pha, name, cell) \ + DT_PROP(node_id, pha##_NAME_##name##_VAL_##cell) + +/** + * @brief Like DT_PHA_BY_NAME(), but with a fallback to default_value + * + * If the value exists, this expands to DT_PHA_BY_NAME(node_id, pha, + * name, cell). The default_value parameter is not expanded in this case. + * + * Otherwise, this expands to default_value. + * + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param name lowercase-and-underscores name of a specifier in "pha" + * @param cell lowercase-and-underscores cell name in the named specifier + * @param default_value a fallback value to expand to + * @return the cell's value or default_value + */ +#define DT_PHA_BY_NAME_OR(node_id, pha, name, cell, default_value) \ + DT_PROP_OR(node_id, pha##_NAME_##name##_VAL_##cell, default_value) +/* Implementation note: the _NAME_##name##_VAL_##cell##_EXISTS + * macros are defined, so it's safe to use DT_PROP_OR() here, because + * that uses an IS_ENABLED() on the _EXISTS macro. + */ + +/** + * @brief Get a phandle's node identifier from a phandle array by name + * + * It might help to read the argument order as being similar to + * "node->phandle_struct.name.phandle". That is, the phandle array is + * treated as a structure with named elements. The return value is + * the node identifier for a phandle inside the structure. + * + * Example devicetree fragment: + * + * adc1: adc@... { + * label = "ADC_1"; + * }; + * + * adc2: adc@... { + * label = "ADC_2"; + * }; + * + * n: node { + * io-channels = <&adc1 10>, <&adc2 20>; + * io-channel-names = "SENSOR", "BANDGAP"; + * }; + * + * Above, "io-channels" has two elements: + * + * - the element named "SENSOR" has phandle &adc1 + * - the element named "BANDGAP" has phandle &adc2 + * + * Example usage: + * + * #define NODE DT_NODELABEL(n) + * + * DT_LABEL(DT_PHANDLE_BY_NAME(NODE, io_channels, sensor)) // "ADC_1" + * DT_LABEL(DT_PHANDLE_BY_NAME(NODE, io_channels, bandgap)) // "ADC_2" + * + * Notice how devicetree properties and names are lowercased, and + * non-alphanumeric characters are converted to underscores. + * + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param name lowercase-and-underscores name of an element in "pha" + * @return a node identifier for the node with that phandle + */ +#define DT_PHANDLE_BY_NAME(node_id, pha, name) \ + DT_PROP(node_id, pha##_NAME_##name##_PH) + +/** + * @brief Get a node identifier for a phandle in a property. + * + * When a node's value at a logical index contains a phandle, this + * macro returns a node identifier for the node with that phandle. + * + * Therefore, if "prop" has type "phandle", "idx" must be zero. (A + * "phandle" type is treated as a "phandles" with a fixed length of + * 1). + * + * Example devicetree fragment: + * + * n1: node-1 { + * foo = <&n2 &n3>; + * }; + * + * n2: node-2 { ... }; + * n3: node-3 { ... }; + * + * Above, "foo" has type phandles and has two elements: + * + * - index 0 has phandle &n2, which is node-2's phandle + * - index 1 has phandle &n3, which is node-3's phandle + * + * Example usage: + * + * #define N1 DT_NODELABEL(n1) + * + * DT_PHANDLE_BY_IDX(N1, foo, 0) // node identifier for node-2 + * DT_PHANDLE_BY_IDX(N1, foo, 1) // node identifier for node-3 + * + * Behavior is analogous for phandle-arrays. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name in "node_id" + * with type "phandle", "phandles" or "phandle-array" + * @param idx index into "prop" + * @return node identifier for the node with the phandle at that index + */ +#define DT_PHANDLE_BY_IDX(node_id, prop, idx) \ + DT_CAT6(node_id, _P_, prop, _IDX_, idx, _PH) +/* + * Implementation note: using DT_CAT6 above defers concatenation until + * after expansion of each parameter. This is important when 'idx' is + * expandable to a number, but it isn't one "yet". + */ + +/** + * @brief Get a node identifier for a phandle property's value + * + * This is equivalent to DT_PHANDLE_BY_IDX(node_id, prop, 0). Its primary + * benefit is readability when "prop" has type "phandle". + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property of "node_id" + * with type "phandle" + * @return a node identifier for the node pointed to by "ph" + */ +#define DT_PHANDLE(node_id, prop) DT_PHANDLE_BY_IDX(node_id, prop, 0) + +/** + * @} + */ + +/** + * @defgroup devicetree-reg-prop reg property + * @ingroup devicetree + * @{ + */ + +/** + * @brief Get the number of register blocks in the reg property + * + * Use this instead of DT_PROP_LEN(node_id, reg). + * @param node_id node identifier + * @return Number of register blocks in the node's "reg" property. + */ +#define DT_NUM_REGS(node_id) DT_CAT(node_id, _REG_NUM) + +/** + * @brief Is "idx" a valid register block index? + * + * If this returns 1, then DT_REG_ADDR_BY_IDX(node_id, idx) or + * DT_REG_SIZE_BY_IDX(node_id, idx) are valid. + * If it returns 0, it is an error to use those macros with index "idx". + * @param node_id node identifier + * @param idx index to check + * @return 1 if "idx" is a valid register block index, + * 0 otherwise. + */ +#define DT_REG_HAS_IDX(node_id, idx) \ + IS_ENABLED(DT_CAT(node_id, _REG_IDX_##idx##_EXISTS)) + +/** + * @brief Get the base address of the register block at index "idx" + * @param node_id node identifier + * @param idx index of the register whose address to return + * @return address of the idx-th register block + */ +#define DT_REG_ADDR_BY_IDX(node_id, idx) \ + DT_CAT(node_id, _REG_IDX_##idx##_VAL_ADDRESS) + +/** + * @brief Get the size of the register block at index "idx" + * + * This is the size of an individual register block, not the total + * number of register blocks in the property; use DT_NUM_REGS() for + * that. + * + * @param node_id node identifier + * @param idx index of the register whose size to return + * @return size of the idx-th register block + */ +#define DT_REG_SIZE_BY_IDX(node_id, idx) \ + DT_CAT(node_id, _REG_IDX_##idx##_VAL_SIZE) + +/** + * @brief Get a node's (only) register block address + * + * Equivalent to DT_REG_ADDR_BY_IDX(node_id, 0). + * @param node_id node identifier + * @return node's register block address + */ +#define DT_REG_ADDR(node_id) DT_REG_ADDR_BY_IDX(node_id, 0) + +/** + * @brief Get a node's (only) register block size + * + * Equivalent to DT_REG_SIZE_BY_IDX(node_id, 0). + * @param node_id node identifier + * @return node's only register block's size + */ +#define DT_REG_SIZE(node_id) DT_REG_SIZE_BY_IDX(node_id, 0) + +/** + * @brief Get a register block's base address by name + * @param node_id node identifier + * @param name lowercase-and-underscores register specifier name + * @return address of the register block specified by name + */ +#define DT_REG_ADDR_BY_NAME(node_id, name) \ + DT_CAT(node_id, _REG_NAME_##name##_VAL_ADDRESS) + +/** + * @brief Get a register block's size by name + * @param node_id node identifier + * @param name lowercase-and-underscores register specifier name + * @return size of the register block specified by name + */ +#define DT_REG_SIZE_BY_NAME(node_id, name) \ + DT_CAT(node_id, _REG_NAME_##name##_VAL_SIZE) + +/** + * @} + */ + +/** + * @defgroup devicetree-interrupts-prop interrupts property + * @ingroup devicetree + * @{ + */ + +/** + * @brief Get the number of interrupt sources for the node + * + * Use this instead of DT_PROP_LEN(node_id, interrupts). + * + * @param node_id node identifier + * @return Number of interrupt specifiers in the node's "interrupts" property. + */ +#define DT_NUM_IRQS(node_id) DT_CAT(node_id, _IRQ_NUM) + +/** + * @brief Is "idx" a valid interrupt index? + * + * If this returns 1, then DT_IRQ_BY_IDX(node_id, idx) is valid. + * If it returns 0, it is an error to use that macro with this index. + * @param node_id node identifier + * @param idx index to check + * @return 1 if the idx is valid for the interrupt property + * 0 otherwise. + */ +#define DT_IRQ_HAS_IDX(node_id, idx) \ + IS_ENABLED(DT_CAT(node_id, _IRQ_IDX_##idx##_EXISTS)) + +/** + * @brief Does an interrupts property have a named cell specifier at an index? + * If this returns 1, then DT_IRQ_BY_IDX(node_id, idx, cell) is valid. + * If it returns 0, it is an error to use that macro. + * @param node_id node identifier + * @param idx index to check + * @param cell named cell value whose existence to check + * @return 1 if the named cell exists in the interrupt specifier at index idx + * 0 otherwise. + */ +#define DT_IRQ_HAS_CELL_AT_IDX(node_id, idx, cell) \ + IS_ENABLED(DT_CAT(node_id, _IRQ_IDX_##idx##_VAL_##cell##_EXISTS)) + +/** + * @brief Equivalent to DT_IRQ_HAS_CELL_AT_IDX(node_id, 0, cell) + * @param node_id node identifier + * @param cell named cell value whose existence to check + * @return 1 if the named cell exists in the interrupt specifier at index 0 + * 0 otherwise. + */ +#define DT_IRQ_HAS_CELL(node_id, cell) DT_IRQ_HAS_CELL_AT_IDX(node_id, 0, cell) + +/** + * @brief Does an interrupts property have a named specifier value at an index? + * If this returns 1, then DT_IRQ_BY_NAME(node_id, name, cell) is valid. + * If it returns 0, it is an error to use that macro. + * @param node_id node identifier + * @param name lowercase-and-underscores interrupt specifier name + * @return 1 if "name" is a valid named specifier + * 0 otherwise. + */ +#define DT_IRQ_HAS_NAME(node_id, name) \ + IS_ENABLED(DT_CAT(node_id, _IRQ_NAME_##name##_VAL_irq_EXISTS)) + +/** + * @brief Get a value within an interrupt specifier at an index + * + * It might help to read the argument order as being similar to + * "node->interrupts[index].cell". + * + * This can be used to get information about an individual interrupt + * when a device generates more than one. + * + * Example devicetree fragment: + * + * my-serial: serial@... { + * interrupts = < 33 0 >, < 34 1 >; + * }; + * + * Assuming the node's interrupt domain has "#interrupt-cells = <2>;" and + * the individual cells in each interrupt specifier are named "irq" and + * "priority" by the node's binding, here are some examples: + * + * #define SERIAL DT_NODELABEL(my_serial) + * + * Example usage Value + * ------------- ----- + * DT_IRQ_BY_IDX(SERIAL, 0, irq) 33 + * DT_IRQ_BY_IDX(SERIAL, 0, priority) 0 + * DT_IRQ_BY_IDX(SERIAL, 1, irq, 34 + * DT_IRQ_BY_IDX(SERIAL, 1, priority) 1 + * + * @param node_id node identifier + * @param idx logical index into the interrupt specifier array + * @param cell cell name specifier + * @return the named value at the specifier given by the index + */ +#define DT_IRQ_BY_IDX(node_id, idx, cell) \ + DT_CAT(node_id, _IRQ_IDX_##idx##_VAL_##cell) + +/** + * @brief Get a value within an interrupt specifier by name + * + * It might help to read the argument order as being similar to + * "node->interrupts.name.cell". + * + * This can be used to get information about an individual interrupt + * when a device generates more than one, if the bindings give each + * interrupt specifier a name. + * + * @param node_id node identifier + * @param name lowercase-and-underscores interrupt specifier name + * @param cell cell name specifier + * @return the named value at the specifier given by the index + */ +#define DT_IRQ_BY_NAME(node_id, name, cell) \ + DT_CAT(node_id, _IRQ_NAME_##name##_VAL_##cell) + +/** + * @brief Get an interrupt specifier's value + * Equivalent to DT_IRQ_BY_IDX(node_id, 0, cell). + * @param node_id node identifier + * @param cell cell name specifier + * @return the named value at that index + */ +#define DT_IRQ(node_id, cell) DT_IRQ_BY_IDX(node_id, 0, cell) + +/** + * @brief Get a node's (only) irq number + * + * Equivalent to DT_IRQ(node_id, irq). This is provided as a convenience + * for the common case where a node generates exactly one interrupt, + * and the IRQ number is in a cell named "irq". + * + * @param node_id node identifier + * @return the interrupt number for the node's only interrupt + */ +#define DT_IRQN(node_id) DT_IRQ(node_id, irq) + +/** + * @} + */ + +/** + * @defgroup devicetree-generic-chosen Chosen nodes + * @ingroup devicetree + * @{ + */ + +/** + * @brief Get a node identifier for a /chosen node property + * + * This is only valid to call if DT_HAS_CHOSEN(prop) is 1. + * @param prop lowercase-and-underscores property name for + * the /chosen node + * @return a node identifier for the chosen node property + */ +#define DT_CHOSEN(prop) DT_CAT(DT_CHOSEN_, prop) + +/** + * @brief Test if the devicetree has a /chosen node + * @param prop lowercase-and-underscores devicetree property + * @return 1 if the chosen property exists and refers to a node, + * 0 otherwise + */ +#define DT_HAS_CHOSEN(prop) IS_ENABLED(DT_CHOSEN_##prop##_EXISTS) + +/** + * @} + */ + +/** + * @defgroup devicetree-generic-foreach "For-each" macros + * @ingroup devicetree + * @{ + */ + +/** + * @brief Invokes "fn" for each child of "node_id" + * + * The macro "fn" must take one parameter, which will be the node + * identifier of a child node of "node_id". + * + * Example devicetree fragment: + * + * n: node { + * child-1 { + * label = "foo"; + * }; + * child-2 { + * label = "bar"; + * }; + * }; + * + * Example usage: + * + * #define LABEL_AND_COMMA(node_id) DT_LABEL(node_id), + * + * const char *child_labels[] = { + * DT_FOREACH_CHILD(DT_NODELABEL(n), LABEL_AND_COMMA) + * }; + * + * This expands to: + * + * const char *child_labels[] = { + * "foo", "bar", + * }; + * + * @param node_id node identifier + * @param fn macro to invoke + */ +#define DT_FOREACH_CHILD(node_id, fn) \ + DT_CAT(node_id, _FOREACH_CHILD)(fn) + +/** + * @brief Invokes "fn" for each child of "node_id" with multiple arguments + * + * The macro "fn" takes multiple arguments. The first should be the node + * identifier for the child node. The remaining are passed-in by the caller. + * + * @param node_id node identifier + * @param fn macro to invoke + * @param ... variable number of arguments to pass to fn + * + * @see DT_FOREACH_CHILD + */ +#define DT_FOREACH_CHILD_VARGS(node_id, fn, ...) \ + DT_CAT(node_id, _FOREACH_CHILD_VARGS)(fn, __VA_ARGS__) + +/** + * @brief Call "fn" on the child nodes with status "okay" + * + * The macro "fn" should take one argument, which is the node + * identifier for the child node. + * + * As usual, both a missing status and an "ok" status are + * treated as "okay". + * + * @param node_id node identifier + * @param fn macro to invoke + */ +#define DT_FOREACH_CHILD_STATUS_OKAY(node_id, fn) \ + DT_CAT(node_id, _FOREACH_CHILD_STATUS_OKAY)(fn) + +/** + * @brief Call "fn" on the child nodes with status "okay" with multiple + * arguments + * + * The macro "fn" takes multiple arguments. The first should be the node + * identifier for the child node. The remaining are passed-in by the caller. + * + * As usual, both a missing status and an "ok" status are + * treated as "okay". + * + * @param node_id node identifier + * @param fn macro to invoke + * @param ... variable number of arguments to pass to fn + * + * @see DT_FOREACH_CHILD_STATUS_OKAY + */ +#define DT_FOREACH_CHILD_STATUS_OKAY_VARGS(node_id, fn, ...) \ + DT_CAT(node_id, _FOREACH_CHILD_STATUS_OKAY_VARGS)(fn, __VA_ARGS__) + +/** + * @brief Invokes "fn" for each element in the value of property "prop". + * + * The macro "fn" must take three parameters: fn(node_id, prop, idx). + * "node_id" and "prop" are the same as what is passed to + * DT_FOREACH_PROP_ELEM, and "idx" is the current index into the array. + * The "idx" values are integer literals starting from 0. + * + * Example devicetree fragment: + * + * n: node { + * my-ints = <1 2 3>; + * }; + * + * Example usage: + * + * #define TIMES_TWO(node_id, prop, idx) \ + * (2 * DT_PROP_BY_IDX(node_id, prop, idx)), + * + * int array[] = { + * DT_FOREACH_PROP_ELEM(DT_NODELABEL(n), my_ints, TIMES_TWO) + * }; + * + * This expands to: + * + * int array[] = { + * (2 * 1), (2 * 2), (2 * 3), + * }; + * + * In general, this macro expands to: + * + * fn(node_id, prop, 0) fn(node_id, prop, 1) [...] fn(node_id, prop, n-1) + * + * where "n" is the number of elements in "prop", as it would be + * returned by DT_PROP_LEN(node_id, prop). + * + * The "prop" argument must refer to a property with type string, + * array, uint8-array, string-array, phandles, or phandle-array. It is + * an error to use this macro with properties of other types. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name + * @param fn macro to invoke + */ +#define DT_FOREACH_PROP_ELEM(node_id, prop, fn) \ + DT_CAT4(node_id, _P_, prop, _FOREACH_PROP_ELEM)(fn) + +/** + * @brief Invokes "fn" for each element in the value of property "prop" with + * multiple arguments. + * + * The macro "fn" must take multiple parameters: fn(node_id, prop, idx, ...). + * "node_id" and "prop" are the same as what is passed to + * DT_FOREACH_PROP_ELEM, and "idx" is the current index into the array. + * The "idx" values are integer literals starting from 0. The remaining + * arguments are passed-in by the caller. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name + * @param fn macro to invoke + * @param ... variable number of arguments to pass to fn + * + * @see DT_FOREACH_PROP_ELEM + */ +#define DT_FOREACH_PROP_ELEM_VARGS(node_id, prop, fn, ...) \ + DT_CAT4(node_id, _P_, prop, _FOREACH_PROP_ELEM_VARGS)(fn, __VA_ARGS__) + +/** + * @brief Call "fn" on all nodes with compatible DT_DRV_COMPAT + * and status "okay" + * + * This macro expands to: + * + * fn(node_id_1) fn(node_id_2) ... fn(node_id_n) + * + * where each "node_id_" is a node identifier for some node with + * compatible "compat" and status "okay". Whitespace is added between + * expansions as shown above. + * + * Example devicetree fragment: + * + * / { + * a { + * compatible = "foo"; + * status = "okay"; + * }; + * b { + * compatible = "foo"; + * status = "disabled"; + * }; + * c { + * compatible = "foo"; + * }; + * }; + * + * Example usage: + * + * DT_FOREACH_STATUS_OKAY(foo, DT_NODE_PATH) + * + * This expands to one of the following: + * + * "/a" "/c" + * "/c" "/a" + * + * "One of the following" is because no guarantees are made about the + * order that node identifiers are passed to "fn" in the expansion. + * + * (The "/c" string literal is present because a missing status + * property is always treated as if the status were set to "okay".) + * + * Note also that "fn" is responsible for adding commas, semicolons, + * or other terminators as needed. + * + * @param compat lowercase-and-underscores devicetree compatible + * @param fn Macro to call for each enabled node. Must accept a + * node_id as its only parameter. + */ +#define DT_FOREACH_STATUS_OKAY(compat, fn) \ + COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(compat), \ + (UTIL_CAT(DT_FOREACH_OKAY_, compat)(fn)), \ + ()) + +/** + * @brief Invokes "fn" for each status "okay" node of a compatible + * with multiple arguments. + * + * This is like DT_FOREACH_STATUS_OKAY() except you can also pass + * additional arguments to "fn". + * + * Example devicetree fragment: + * + * / { + * a { + * compatible = "foo"; + * val = <3>; + * }; + * b { + * compatible = "foo"; + * val = <4>; + * }; + * }; + * + * Example usage: + * + * #define MY_FN(node_id, operator) DT_PROP(node_id, val) operator + * x = DT_FOREACH_STATUS_OKAY_VARGS(foo, MY_FN, +) 0; + * + * This expands to one of the following: + * + * x = 3 + 4 + 0; + * x = 4 + 3 + 0; + * + * i.e. it sets x to 7. As with DT_FOREACH_STATUS_OKAY(), there are no + * guarantees about the order nodes appear in the expansion. + * + * @param compat lowercase-and-underscores devicetree compatible + * @param fn Macro to call for each enabled node. Must accept a + * node_id as its only parameter. + * @param ... Additional arguments to pass to "fn" + */ +#define DT_FOREACH_STATUS_OKAY_VARGS(compat, fn, ...) \ + COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(compat), \ + (UTIL_CAT(DT_FOREACH_OKAY_VARGS_, \ + compat)(fn, __VA_ARGS__)), \ + ()) + +/** + * @} + */ + +/** + * @defgroup devicetree-generic-exist Existence checks + * @ingroup devicetree + * @{ + */ + +/** + * @brief Does a node identifier refer to a node? + * + * Tests whether a node identifier refers to a node which exists, i.e. + * is defined in the devicetree. + * + * It doesn't matter whether or not the node has a matching binding, + * or what the node's status value is. This is purely a check of + * whether the node exists at all. + * + * @param node_id a node identifier + * @return 1 if the node identifier refers to a node, + * 0 otherwise. + */ +#define DT_NODE_EXISTS(node_id) IS_ENABLED(DT_CAT(node_id, _EXISTS)) + +/** + * @brief Does a node identifier refer to a node with a status? + * + * Example uses: + * + * DT_NODE_HAS_STATUS(DT_PATH(soc, i2c_12340000), okay) + * DT_NODE_HAS_STATUS(DT_PATH(soc, i2c_12340000), disabled) + * + * Tests whether a node identifier refers to a node which: + * + * - exists in the devicetree, and + * - has a status property matching the second argument + * (except that either a missing status or an "ok" status + * in the devicetree is treated as if it were "okay" instead) + * + * @param node_id a node identifier + * @param status a status as one of the tokens okay or disabled, not a string + * @return 1 if the node has the given status, 0 otherwise. + */ +#define DT_NODE_HAS_STATUS(node_id, status) \ + DT_NODE_HAS_STATUS_INTERNAL(node_id, status) + +/** + * @brief Does the devicetree have a status "okay" node with a compatible? + * + * Test for whether the devicetree has any nodes with status "okay" + * and the given compatible. That is, this returns 1 if and only if + * there is at least one "node_id" for which both of these + * expressions return 1: + * + * DT_NODE_HAS_STATUS(node_id, okay) + * DT_NODE_HAS_COMPAT(node_id, compat) + * + * As usual, both a missing status and an "ok" status are treated as + * "okay". + * + * @param compat lowercase-and-underscores compatible, without quotes + * @return 1 if both of the above conditions are met, 0 otherwise + */ +#define DT_HAS_COMPAT_STATUS_OKAY(compat) \ + IS_ENABLED(DT_CAT(DT_COMPAT_HAS_OKAY_, compat)) + +/** + * @brief Get the number of instances of a given compatible with + * status "okay" + * @param compat lowercase-and-underscores compatible, without quotes + * @return Number of instances with status "okay" + */ +#define DT_NUM_INST_STATUS_OKAY(compat) \ + UTIL_AND(DT_HAS_COMPAT_STATUS_OKAY(compat), \ + UTIL_CAT(DT_N_INST, DT_DASH(compat, NUM_OKAY))) + +/** + * @brief Does a devicetree node match a compatible? + * + * Example devicetree fragment: + * + * n: node { + * compatible = "vnd,specific-device", "generic-device"; + * } + * + * Example usages which evaluate to 1: + * + * DT_NODE_HAS_COMPAT(DT_NODELABEL(n), vnd_specific_device) + * DT_NODE_HAS_COMPAT(DT_NODELABEL(n), generic_device) + * + * This macro only uses the value of the compatible property. Whether + * or not a particular compatible has a matching binding has no effect + * on its value, nor does the node's status. + * + * @param node_id node identifier + * @param compat lowercase-and-underscores compatible, without quotes + * @return 1 if the node's compatible property contains compat, + * 0 otherwise. + */ +#define DT_NODE_HAS_COMPAT(node_id, compat) \ + IS_ENABLED(DT_CAT(node_id, _COMPAT_MATCHES_##compat)) + +/** + * @brief Does a devicetree node have a compatible and status? + * + * This is equivalent to: + * + * (DT_NODE_HAS_COMPAT(node_id, compat) && + * DT_NODE_HAS_STATUS(node_id, status)) + * + * @param node_id node identifier + * @param compat lowercase-and-underscores compatible, without quotes + * @param status okay or disabled as a token, not a string + */ +#define DT_NODE_HAS_COMPAT_STATUS(node_id, compat, status) \ + DT_NODE_HAS_COMPAT(node_id, compat) && DT_NODE_HAS_STATUS(node_id, status) + +/** + * @brief Does a devicetree node have a property? + * + * Tests whether a devicetree node has a property defined. + * + * This tests whether the property is defined at all, not whether a + * boolean property is true or false. To get a boolean property's + * truth value, use DT_PROP(node_id, prop) instead. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name + * @return 1 if the node has the property, 0 otherwise. + */ +#define DT_NODE_HAS_PROP(node_id, prop) \ + IS_ENABLED(DT_CAT(node_id, _P_##prop##_EXISTS)) + + +/** + * @brief Does a phandle array have a named cell specifier at an index? + * + * If this returns 1, then the phandle-array property "pha" has a cell + * named "cell" at index "idx", and therefore DT_PHA_BY_IDX(node_id, + * pha, idx, cell) is valid. If it returns 0, it's an error to use + * DT_PHA_BY_IDX() with the same arguments. + * + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param idx index to check within "pha" + * @param cell lowercase-and-underscores cell name whose existence to check + * at index "idx" + * @return 1 if the named cell exists in the specifier at index idx, + * 0 otherwise. + */ +#define DT_PHA_HAS_CELL_AT_IDX(node_id, pha, idx, cell) \ + IS_ENABLED(DT_PROP(node_id, \ + pha##_IDX_##idx##_VAL_##cell##_EXISTS)) + +/** + * @brief Equivalent to DT_PHA_HAS_CELL_AT_IDX(node_id, pha, 0, cell) + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param cell lowercase-and-underscores cell name whose existence to check + * at index "idx" + * @return 1 if the named cell exists in the specifier at index 0, + * 0 otherwise. + */ +#define DT_PHA_HAS_CELL(node_id, pha, cell) \ + DT_PHA_HAS_CELL_AT_IDX(node_id, pha, 0, cell) + +/** + * @} + */ + +/** + * @defgroup devicetree-generic-bus Bus helpers + * @ingroup devicetree + * @{ + */ + +/** + * @brief Node's bus controller + * + * Get the node identifier of the node's bus controller. This can be + * used with @ref DT_PROP() to get properties of the bus controller. + * + * It is an error to use this with nodes which do not have bus + * controllers. + * + * Example devicetree fragment: + * + * i2c@deadbeef { + * label = "I2C_CTLR"; + * status = "okay"; + * clock-frequency = < 100000 >; + * + * i2c_device: accelerometer@12 { + * ... + * }; + * }; + * + * Example usage: + * + * DT_PROP(DT_BUS(DT_NODELABEL(i2c_device)), clock_frequency) // 100000 + * + * @param node_id node identifier + * @return a node identifier for the node's bus controller + */ +#define DT_BUS(node_id) DT_CAT(node_id, _BUS) + +/** + * @brief Node's bus controller's label property + * @param node_id node identifier + * @return the label property of the node's bus controller DT_BUS(node) + */ +#define DT_BUS_LABEL(node_id) DT_PROP(DT_BUS(node_id), label) + +/** + * @brief Is a node on a bus of a given type? + * + * Example devicetree overlay: + * + * &i2c0 { + * temp: temperature-sensor@76 { + * compatible = "vnd,some-sensor"; + * reg = <0x76>; + * }; + * }; + * + * Example usage, assuming "i2c0" is an I2C bus controller node, and + * therefore "temp" is on an I2C bus: + * + * DT_ON_BUS(DT_NODELABEL(temp), i2c) // 1 + * DT_ON_BUS(DT_NODELABEL(temp), spi) // 0 + * + * @param node_id node identifier + * @param bus lowercase-and-underscores bus type as a C token (i.e. + * without quotes) + * @return 1 if the node is on a bus of the given type, + * 0 otherwise + */ +#define DT_ON_BUS(node_id, bus) IS_ENABLED(DT_CAT(node_id, _BUS_##bus)) + +/** + * @} + */ + +/** + * @defgroup devicetree-inst Instance-based devicetree APIs + * @ingroup devicetree + * @{ + */ + +/** + * @brief Node identifier for an instance of a DT_DRV_COMPAT compatible + * @param inst instance number + * @return a node identifier for the node with DT_DRV_COMPAT compatible and + * instance number "inst" + */ +#define DT_DRV_INST(inst) DT_INST(inst, DT_DRV_COMPAT) + +/** + * @brief Call "fn" on all child nodes of DT_DRV_INST(inst). + * + * The macro "fn" should take one argument, which is the node + * identifier for the child node. + * + * @param inst instance number + * @param fn macro to invoke on each child node identifier + * + * @see DT_FOREACH_CHILD + */ +#define DT_INST_FOREACH_CHILD(inst, fn) \ + DT_FOREACH_CHILD(DT_DRV_INST(inst), fn) + +/** + * @brief Call "fn" on all child nodes of DT_DRV_INST(inst). + * + * The macro "fn" takes multiple arguments. The first should be the node + * identifier for the child node. The remaining are passed-in by the caller. + * + * @param inst instance number + * @param fn macro to invoke on each child node identifier + * @param ... variable number of arguments to pass to fn + * + * @see DT_FOREACH_CHILD + */ +#define DT_INST_FOREACH_CHILD_VARGS(inst, fn, ...) \ + DT_FOREACH_CHILD_VARGS(DT_DRV_INST(inst), fn, __VA_ARGS__) + +/** + * @brief Get a DT_DRV_COMPAT instance property + * @param inst instance number + * @param prop lowercase-and-underscores property name + * @return a representation of the property's value + */ +#define DT_INST_PROP(inst, prop) DT_PROP(DT_DRV_INST(inst), prop) + +/** + * @brief Get a DT_DRV_COMPAT property length + * @param inst instance number + * @param prop lowercase-and-underscores property name + * @return logical length of the property + */ +#define DT_INST_PROP_LEN(inst, prop) DT_PROP_LEN(DT_DRV_INST(inst), prop) + +/** + * @brief Is index "idx" valid for an array type property + * on a DT_DRV_COMPAT instance? + * @param inst instance number + * @param prop lowercase-and-underscores property name + * @param idx index to check + * @return 1 if "idx" is a valid index into the given property, + * 0 otherwise. + */ +#define DT_INST_PROP_HAS_IDX(inst, prop, idx) \ + DT_PROP_HAS_IDX(DT_DRV_INST(inst), prop, idx) + +/** + * @brief Get a DT_DRV_COMPAT element value in an array property + * @param inst instance number + * @param prop lowercase-and-underscores property name + * @param idx the index to get + * @return a representation of the idx-th element of the property + */ +#define DT_INST_PROP_BY_IDX(inst, prop, idx) \ + DT_PROP_BY_IDX(DT_DRV_INST(inst), prop, idx) + +/** + * @brief Like DT_INST_PROP(), but with a fallback to default_value + * @param inst instance number + * @param prop lowercase-and-underscores property name + * @param default_value a fallback value to expand to + * @return DT_INST_PROP(inst, prop) or default_value + */ +#define DT_INST_PROP_OR(inst, prop, default_value) \ + DT_PROP_OR(DT_DRV_INST(inst), prop, default_value) + +/** + * @brief Get a DT_DRV_COMPAT instance's "label" property + * @param inst instance number + * @return instance's label property value + */ +#define DT_INST_LABEL(inst) DT_INST_PROP(inst, label) + +/** + * @brief Get a DT_DRV_COMPAT instance's property value from a phandle's node + * @param inst instance number + * @param ph lowercase-and-underscores property of "inst" + * with type "phandle" + * @param prop lowercase-and-underscores property of the phandle's node + * @return the value of "prop" as described in the DT_PROP() documentation + */ +#define DT_INST_PROP_BY_PHANDLE(inst, ph, prop) \ + DT_INST_PROP_BY_PHANDLE_IDX(inst, ph, 0, prop) + +/** + * @brief Get a DT_DRV_COMPAT instance's property value from a phandle in a + * property. + * @param inst instance number + * @param phs lowercase-and-underscores property with type "phandle", + * "phandles", or "phandle-array" + * @param idx logical index into "phs", which must be zero if "phs" + * has type "phandle" + * @param prop lowercase-and-underscores property of the phandle's node + * @return the value of "prop" as described in the DT_PROP() documentation + */ +#define DT_INST_PROP_BY_PHANDLE_IDX(inst, phs, idx, prop) \ + DT_PROP_BY_PHANDLE_IDX(DT_DRV_INST(inst), phs, idx, prop) + +/** + * @brief Get a DT_DRV_COMPAT instance's phandle-array specifier value at an index + * @param inst instance number + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param idx logical index into the property "pha" + * @param cell binding's cell name within the specifier at index "idx" + * @return the value of the cell inside the specifier at index "idx" + */ +#define DT_INST_PHA_BY_IDX(inst, pha, idx, cell) \ + DT_PHA_BY_IDX(DT_DRV_INST(inst), pha, idx, cell) + +/** + * @brief Like DT_INST_PHA_BY_IDX(), but with a fallback to default_value + * @param inst instance number + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param idx logical index into the property "pha" + * @param cell binding's cell name within the specifier at index "idx" + * @param default_value a fallback value to expand to + * @return DT_INST_PHA_BY_IDX(inst, pha, idx, cell) or default_value + */ +#define DT_INST_PHA_BY_IDX_OR(inst, pha, idx, cell, default_value) \ + DT_PHA_BY_IDX_OR(DT_DRV_INST(inst), pha, idx, cell, default_value) + +/** + * @brief Get a DT_DRV_COMPAT instance's phandle-array specifier value + * Equivalent to DT_INST_PHA_BY_IDX(inst, pha, 0, cell) + * @param inst instance number + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param cell binding's cell name for the specifier at "pha" index 0 + * @return the cell value + */ +#define DT_INST_PHA(inst, pha, cell) DT_INST_PHA_BY_IDX(inst, pha, 0, cell) + +/** + * @brief Like DT_INST_PHA(), but with a fallback to default_value + * @param inst instance number + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param cell binding's cell name for the specifier at "pha" index 0 + * @param default_value a fallback value to expand to + * @return DT_INST_PHA(inst, pha, cell) or default_value + */ +#define DT_INST_PHA_OR(inst, pha, cell, default_value) \ + DT_INST_PHA_BY_IDX_OR(inst, pha, 0, cell, default_value) + +/** + * @brief Get a DT_DRV_COMPAT instance's value within a phandle-array + * specifier by name + * @param inst instance number + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param name lowercase-and-underscores name of a specifier in "pha" + * @param cell binding's cell name for the named specifier + * @return the cell value + */ +#define DT_INST_PHA_BY_NAME(inst, pha, name, cell) \ + DT_PHA_BY_NAME(DT_DRV_INST(inst), pha, name, cell) + +/** + * @brief Like DT_INST_PHA_BY_NAME(), but with a fallback to default_value + * @param inst instance number + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param name lowercase-and-underscores name of a specifier in "pha" + * @param cell binding's cell name for the named specifier + * @param default_value a fallback value to expand to + * @return DT_INST_PHA_BY_NAME(inst, pha, name, cell) or default_value + */ +#define DT_INST_PHA_BY_NAME_OR(inst, pha, name, cell, default_value) \ + DT_PHA_BY_NAME_OR(DT_DRV_INST(inst), pha, name, cell, default_value) + +/** + * @brief Get a DT_DRV_COMPAT instance's phandle node identifier from a + * phandle array by name + * @param inst instance number + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param name lowercase-and-underscores name of an element in "pha" + * @return node identifier for the phandle at the element named "name" + */ +#define DT_INST_PHANDLE_BY_NAME(inst, pha, name) \ + DT_PHANDLE_BY_NAME(DT_DRV_INST(inst), pha, name) \ + +/** + * @brief Get a DT_DRV_COMPAT instance's node identifier for a phandle in + * a property. + * @param inst instance number + * @param prop lowercase-and-underscores property name in "inst" + * with type "phandle", "phandles" or "phandle-array" + * @param idx index into "prop" + * @return a node identifier for the phandle at index "idx" in "prop" + */ +#define DT_INST_PHANDLE_BY_IDX(inst, prop, idx) \ + DT_PHANDLE_BY_IDX(DT_DRV_INST(inst), prop, idx) + +/** + * @brief Get a DT_DRV_COMPAT instance's node identifier for a phandle + * property's value + * @param inst instance number + * @param prop lowercase-and-underscores property of "inst" + * with type "phandle" + * @return a node identifier for the node pointed to by "ph" + */ +#define DT_INST_PHANDLE(inst, prop) DT_INST_PHANDLE_BY_IDX(inst, prop, 0) + +/** + * @brief is "idx" a valid register block index on a DT_DRV_COMPAT instance? + * @param inst instance number + * @param idx index to check + * @return 1 if "idx" is a valid register block index, + * 0 otherwise. + */ +#define DT_INST_REG_HAS_IDX(inst, idx) DT_REG_HAS_IDX(DT_DRV_INST(inst), idx) + +/** + * @brief Get a DT_DRV_COMPAT instance's idx-th register block's address + * @param inst instance number + * @param idx index of the register whose address to return + * @return address of the instance's idx-th register block + */ +#define DT_INST_REG_ADDR_BY_IDX(inst, idx) DT_REG_ADDR_BY_IDX(DT_DRV_INST(inst), idx) + +/** + * @brief Get a DT_DRV_COMPAT instance's idx-th register block's size + * @param inst instance number + * @param idx index of the register whose size to return + * @return size of the instance's idx-th register block + */ +#define DT_INST_REG_SIZE_BY_IDX(inst, idx) \ + DT_REG_SIZE_BY_IDX(DT_DRV_INST(inst), idx) + +/** + * @brief Get a DT_DRV_COMPAT's register block address by name + * @param inst instance number + * @param name lowercase-and-underscores register specifier name + * @return address of the register block with the given name + */ +#define DT_INST_REG_ADDR_BY_NAME(inst, name) \ + DT_REG_ADDR_BY_NAME(DT_DRV_INST(inst), name) + +/** + * @brief Get a DT_DRV_COMPAT's register block size by name + * @param inst instance number + * @param name lowercase-and-underscores register specifier name + * @return size of the register block with the given name + */ +#define DT_INST_REG_SIZE_BY_NAME(inst, name) \ + DT_REG_SIZE_BY_NAME(DT_DRV_INST(inst), name) + +/** + * @brief Get a DT_DRV_COMPAT's (only) register block address + * @param inst instance number + * @return instance's register block address + */ +#define DT_INST_REG_ADDR(inst) DT_INST_REG_ADDR_BY_IDX(inst, 0) + +/** + * @brief Get a DT_DRV_COMPAT's (only) register block size + * @param inst instance number + * @return instance's register block size + */ +#define DT_INST_REG_SIZE(inst) DT_INST_REG_SIZE_BY_IDX(inst, 0) + +/** + * @brief Get a DT_DRV_COMPAT interrupt specifier value at an index + * @param inst instance number + * @param idx logical index into the interrupt specifier array + * @param cell cell name specifier + * @return the named value at the specifier given by the index + */ +#define DT_INST_IRQ_BY_IDX(inst, idx, cell) \ + DT_IRQ_BY_IDX(DT_DRV_INST(inst), idx, cell) + +/** + * @brief Get a DT_DRV_COMPAT interrupt specifier value by name + * @param inst instance number + * @param name lowercase-and-underscores interrupt specifier name + * @param cell cell name specifier + * @return the named value at the specifier given by the index + */ +#define DT_INST_IRQ_BY_NAME(inst, name, cell) \ + DT_IRQ_BY_NAME(DT_DRV_INST(inst), name, cell) + +/** + * @brief Get a DT_DRV_COMPAT interrupt specifier's value + * @param inst instance number + * @param cell cell name specifier + * @return the named value at that index + */ +#define DT_INST_IRQ(inst, cell) DT_INST_IRQ_BY_IDX(inst, 0, cell) + +/** + * @brief Get a DT_DRV_COMPAT's (only) irq number + * @param inst instance number + * @return the interrupt number for the node's only interrupt + */ +#define DT_INST_IRQN(inst) DT_INST_IRQ(inst, irq) + +/** + * @brief Get a DT_DRV_COMPAT's bus node identifier + * @param inst instance number + * @return node identifier for the instance's bus node + */ +#define DT_INST_BUS(inst) DT_BUS(DT_DRV_INST(inst)) + +/** + * @brief Get a DT_DRV_COMPAT's bus node's label property + * @param inst instance number + * @return the label property of the instance's bus controller + */ +#define DT_INST_BUS_LABEL(inst) DT_BUS_LABEL(DT_DRV_INST(inst)) + +/** + * @brief Test if a DT_DRV_COMPAT's bus type is a given type + * @param inst instance number + * @param bus a binding's bus type as a C token, lowercased and without quotes + * @return 1 if the given instance is on a bus of the given type, + * 0 otherwise + */ +#define DT_INST_ON_BUS(inst, bus) DT_ON_BUS(DT_DRV_INST(inst), bus) + +/** + * @brief Test if any DT_DRV_COMPAT node is on a bus of a given type + * and has status okay + * + * This is a special-purpose macro which can be useful when writing + * drivers for devices which can appear on multiple buses. One example + * is a sensor device which may be wired on an I2C or SPI bus. + * + * Example devicetree overlay: + * + * &i2c0 { + * temp: temperature-sensor@76 { + * compatible = "vnd,some-sensor"; + * reg = <0x76>; + * }; + * }; + * + * Example usage, assuming "i2c0" is an I2C bus controller node, and + * therefore "temp" is on an I2C bus: + * + * #define DT_DRV_COMPAT vnd_some_sensor + * + * DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) // 1 + * + * @param bus a binding's bus type as a C token, lowercased and without quotes + * @return 1 if any enabled node with that compatible is on that bus type, + * 0 otherwise + */ +#define DT_ANY_INST_ON_BUS_STATUS_OKAY(bus) \ + DT_COMPAT_ON_BUS_INTERNAL(DT_DRV_COMPAT, bus) + +/** + * @brief Call "fn" on all nodes with compatible DT_DRV_COMPAT + * and status "okay" + * + * This macro calls "fn(inst)" on each "inst" number that refers to a + * node with status "okay". Whitespace is added between invocations. + * + * Example devicetree fragment: + * + * a { + * compatible = "vnd,device"; + * status = "okay"; + * label = "DEV_A"; + * }; + * + * b { + * compatible = "vnd,device"; + * status = "okay"; + * label = "DEV_B"; + * }; + * + * c { + * compatible = "vnd,device"; + * status = "disabled"; + * label = "DEV_C"; + * }; + * + * Example usage: + * + * #define DT_DRV_COMPAT vnd_device + * #define MY_FN(inst) DT_INST_LABEL(inst), + * + * DT_INST_FOREACH_STATUS_OKAY(MY_FN) + * + * This expands to: + * + * MY_FN(0) MY_FN(1) + * + * and from there, to either this: + * + * "DEV_A", "DEV_B", + * + * or this: + * + * "DEV_B", "DEV_A", + * + * No guarantees are made about the order that a and b appear in the + * expansion. + * + * Note that "fn" is responsible for adding commas, semicolons, or + * other separators or terminators. + * + * Device drivers should use this macro whenever possible to + * instantiate a struct device for each enabled node in the devicetree + * of the driver's compatible DT_DRV_COMPAT. + * + * @param fn Macro to call for each enabled node. Must accept an + * instance number as its only parameter. + */ +#define DT_INST_FOREACH_STATUS_OKAY(fn) \ + COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT), \ + (UTIL_CAT(DT_FOREACH_OKAY_INST_, \ + DT_DRV_COMPAT)(fn)), \ + ()) + +/** + * @brief Call "fn" on all nodes with compatible DT_DRV_COMPAT + * and status "okay" with multiple arguments + * + * + * @param fn Macro to call for each enabled node. Must accept an + * instance number as its only parameter. + * @param ... variable number of arguments to pass to fn + * + * @see DT_INST_FOREACH_STATUS_OKAY + */ +#define DT_INST_FOREACH_STATUS_OKAY_VARGS(fn, ...) \ + COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT), \ + (UTIL_CAT(DT_FOREACH_OKAY_INST_VARGS_, \ + DT_DRV_COMPAT)(fn, __VA_ARGS__)), \ + ()) + +/** + * @brief Invokes "fn" for each element of property "prop" for + * a DT_DRV_COMPAT instance. + * + * Equivalent to DT_FOREACH_PROP_ELEM(DT_DRV_INST(inst), prop, fn). + * + * @param inst instance number + * @param prop lowercase-and-underscores property name + * @param fn macro to invoke + */ +#define DT_INST_FOREACH_PROP_ELEM(inst, prop, fn) \ + DT_FOREACH_PROP_ELEM(DT_DRV_INST(inst), prop, fn) + +/** + * @brief Invokes "fn" for each element of property "prop" for + * a DT_DRV_COMPAT instance with multiple arguments. + * + * Equivalent to + * DT_FOREACH_PROP_ELEM_VARGS(DT_DRV_INST(inst), prop, fn, __VA_ARGS__) + * + * @param inst instance number + * @param prop lowercase-and-underscores property name + * @param fn macro to invoke + * @param ... variable number of arguments to pass to fn + * + * @see DT_INST_FOREACH_PROP_ELEM + */ +#define DT_INST_FOREACH_PROP_ELEM_VARGS(inst, prop, fn, ...) \ + DT_FOREACH_PROP_ELEM_VARGS(DT_DRV_INST(inst), prop, fn, __VA_ARGS__) + +/** + * @brief Does a DT_DRV_COMPAT instance have a property? + * @param inst instance number + * @param prop lowercase-and-underscores property name + * @return 1 if the instance has the property, 0 otherwise. + */ +#define DT_INST_NODE_HAS_PROP(inst, prop) \ + DT_NODE_HAS_PROP(DT_DRV_INST(inst), prop) + +/** + * @brief Does a phandle array have a named cell specifier at an index + * for a DT_DRV_COMPAT instance? + * @param inst instance number + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param idx index to check + * @param cell named cell value whose existence to check + * @return 1 if the named cell exists in the specifier at index idx, + * 0 otherwise. + */ +#define DT_INST_PHA_HAS_CELL_AT_IDX(inst, pha, idx, cell) \ + DT_PHA_HAS_CELL_AT_IDX(DT_DRV_INST(inst), pha, idx, cell) + +/** + * @brief Does a phandle array have a named cell specifier at index 0 + * for a DT_DRV_COMPAT instance? + * @param inst instance number + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param cell named cell value whose existence to check + * @return 1 if the named cell exists in the specifier at index 0, + * 0 otherwise. + */ +#define DT_INST_PHA_HAS_CELL(inst, pha, cell) \ + DT_INST_PHA_HAS_CELL_AT_IDX(inst, pha, 0, cell) + +/** + * @brief is index valid for interrupt property on a DT_DRV_COMPAT instance? + * @param inst instance number + * @param idx logical index into the interrupt specifier array + * @return 1 if the idx is valid for the interrupt property + * 0 otherwise. + */ +#define DT_INST_IRQ_HAS_IDX(inst, idx) DT_IRQ_HAS_IDX(DT_DRV_INST(inst), idx) + +/** + * @brief Does a DT_DRV_COMPAT instance have an interrupt named cell specifier? + * @param inst instance number + * @param idx index to check + * @param cell named cell value whose existence to check + * @return 1 if the named cell exists in the interrupt specifier at index idx + * 0 otherwise. + */ +#define DT_INST_IRQ_HAS_CELL_AT_IDX(inst, idx, cell) \ + DT_IRQ_HAS_CELL_AT_IDX(DT_DRV_INST(inst), idx, cell) + +/** + * @brief Does a DT_DRV_COMPAT instance have an interrupt value? + * @param inst instance number + * @param cell named cell value whose existence to check + * @return 1 if the named cell exists in the interrupt specifier at index 0 + * 0 otherwise. + */ +#define DT_INST_IRQ_HAS_CELL(inst, cell) \ + DT_INST_IRQ_HAS_CELL_AT_IDX(inst, 0, cell) + +/** + * @brief Does a DT_DRV_COMPAT instance have an interrupt value? + * @param inst instance number + * @param name lowercase-and-underscores interrupt specifier name + * @return 1 if "name" is a valid named specifier + */ +#define DT_INST_IRQ_HAS_NAME(inst, name) \ + DT_IRQ_HAS_NAME(DT_DRV_INST(inst), name) + +/** + * @} + */ + +/** @internal pay no attention to the man behind the curtain! */ +#define DT_PATH_INTERNAL(...) \ + UTIL_CAT(DT_ROOT, MACRO_MAP_CAT(DT_S_PREFIX, __VA_ARGS__)) +/** @internal helper for DT_PATH(): prepends _S_ to a node name */ +#define DT_S_PREFIX(name) _S_##name + +/** + * @internal concatenation helper, 2 arguments + * + * This and the following macros are used to paste things together + * with "##" *after* forcing expansion on each argument. + * + * We could try to use something like UTIL_CAT(), but the compiler + * error messages from the util macros can be extremely long when they + * are misused. This unfortunately happens often with devicetree.h, + * since its macro-based API is fiddly and can be hard to get right. + * + * Keeping things brutally simple here hopefully makes some errors + * easier to read. + */ +#define DT_CAT(a1, a2) a1 ## a2 +/** @internal concatenation helper, 3 arguments */ +#define DT_CAT3(a1, a2, a3) a1 ## a2 ## a3 +/** @internal concatenation helper, 4 arguments */ +#define DT_CAT4(a1, a2, a3, a4) a1 ## a2 ## a3 ## a4 +/** @internal concatenation helper, 5 arguments */ +#define DT_CAT5(a1, a2, a3, a4, a5) a1 ## a2 ## a3 ## a4 ## a5 +/** @internal concatenation helper, 6 arguments */ +#define DT_CAT6(a1, a2, a3, a4, a5, a6) a1 ## a2 ## a3 ## a4 ## a5 ## a6 +/* + * If you need to define a bigger DT_CATN(), do so here. Don't leave + * any "holes" of undefined macros, please. + */ + +/** @internal helper for node identifier macros to expand args */ +#define DT_DASH(...) MACRO_MAP_CAT(DT_DASH_PREFIX, __VA_ARGS__) +/** @internal helper for DT_DASH(): prepends _ to a name */ +#define DT_DASH_PREFIX(name) _##name +/** @internal helper for DT_NODE_HAS_STATUS */ +#define DT_NODE_HAS_STATUS_INTERNAL(node_id, status) \ + IS_ENABLED(DT_CAT(node_id, _STATUS_ ## status)) +/** @internal helper for test cases and DT_ANY_INST_ON_BUS_STATUS_OKAY() */ +#define DT_COMPAT_ON_BUS_INTERNAL(compat, bus) \ + IS_ENABLED(UTIL_CAT(DT_CAT(DT_COMPAT_, compat), _BUS_##bus)) + +#endif /* DEVICETREE_H */ diff --git a/framework/include/fwk_dt_bindings.h b/framework/include/fwk_dt_bindings.h new file mode 100644 index 0000000000000000000000000000000000000000..7e23dcec2b8f508ba18ba5e8bd7185f4520de4fd --- /dev/null +++ b/framework/include/fwk_dt_bindings.h @@ -0,0 +1,21 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef FWK_DT_BINDINGS_H +#define FWK_DT_BINDINGS_H + +/* + * Auto generated module indexes for Device tree inclusion. + * Match indices in main C code. + */ +#include "fwk_dt_bindings_gen.h" + +/* Additional module bindings for DTS use */ + + + +#endif /* FWK_DT_BINDINGS_H */ diff --git a/framework/include/fwk_dt_bindings_gen.h.in b/framework/include/fwk_dt_bindings_gen.h.in new file mode 100644 index 0000000000000000000000000000000000000000..d36a28d0bd285fa1e515a89c2b2c8d991583b5f6 --- /dev/null +++ b/framework/include/fwk_dt_bindings_gen.h.in @@ -0,0 +1,17 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef FWK_DT_BINDINGS_GEN_H +#define FWK_DT_BINDINGS_GEN_H + +/* + * Auto generated module indexes for Device tree inclusion. + * Match indices in main C code. + */ +@SCP_MODULE_DT_BINDING_IDX@ + +#endif /* FWK_DT_BINDINGS_GEN_H */ diff --git a/framework/include/fwk_dt_config_dyn.h.in b/framework/include/fwk_dt_config_dyn.h.in new file mode 100644 index 0000000000000000000000000000000000000000..9f1c23a7f9210d49cb603bae2b1739ac8f6aed8b --- /dev/null +++ b/framework/include/fwk_dt_config_dyn.h.in @@ -0,0 +1,20 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef FWK_DT_CONFIG_DYN_H +#define FWK_DT_CONFIG_DYN_H + +/* + * Auto generated dynamic configuration callbacks for dts configured modules + * Common module config calls dyn_get_element_table_(), which is provided + * by the product specific module code. + * + * Product specific code uses _static_get_element_table() to reference the element + * table defined by device tree macros in the common code. + */ +@SCP_MODULE_DT_DYN_CONFIG@ + +#endif /* FWK_DT_CONFIG_DYN_H */ diff --git a/module/pl011/CMakeLists.txt b/module/pl011/CMakeLists.txt index 27a2799a44f985faee2f4c36fdc1cf27aa0dae07..db2bc68e39259a4b3780c485bc2b63b309d565f1 100644 --- a/module/pl011/CMakeLists.txt +++ b/module/pl011/CMakeLists.txt @@ -13,6 +13,11 @@ target_include_directories(${SCP_MODULE_TARGET} target_sources(${SCP_MODULE_TARGET} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/mod_pl011.c") +if(SCP_MODULE IN_LIST SCP_DT_CONFIG_MODULES_ALL) + target_sources(${SCP_MODULE_TARGET} + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/config_dt_pl011.c") +endif() + if("clock" IN_LIST SCP_MODULES) target_link_libraries(${SCP_MODULE_TARGET} PRIVATE module-clock) endif() diff --git a/module/pl011/src/config_dt_pl011.c b/module/pl011/src/config_dt_pl011.c new file mode 100644 index 0000000000000000000000000000000000000000..e42b0bbe3c015c9c92ef553915df05079cd6bbc2 --- /dev/null +++ b/module/pl011/src/config_dt_pl011.c @@ -0,0 +1,110 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2021, Linaro Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PL011_COMPAT arm_pl011 + +#define PL011_PROP(n, prop) DT_PROP(DT_INST(n, PL011_COMPAT), prop) + +#ifdef BUILD_HAS_MOD_CLOCK + +#define CFG_PL011_ELEMENT_DATA(n) \ + .reg_base = DT_REG_ADDR_BY_IDX(DT_INST(n, PL011_COMPAT), 0), \ + .baud_rate_bps = PL011_PROP(n, current_speed), \ + .clock_rate_hz = PL011_PROP(n, clock_frequency), \ + .clock_id = FWK_ID_NONE_INIT, \ + .pd_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_POWER_DOMAIN, \ + PL011_PROP(n, pd_id) \ + ), + +#else + +#define CFG_PL011_ELEMENT_DATA(n) \ + .reg_base = DT_REG_ADDR_BY_IDX(DT_INST(n, PL011_COMPAT), 0), \ + .baud_rate_bps = PL011_PROP(n, current_speed), \ + .clock_rate_hz = PL011_PROP(n, clock_frequency), \ + .pd_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_POWER_DOMAIN, \ + PL011_PROP(n, pd_id) \ + ), + +#endif + +#define CFG_PL011_ELEMENT_INIT(n) \ + [n] = { \ + .name = PL011_PROP(n, label), \ + .data = &(struct mod_pl011_element_cfg) { \ + CFG_PL011_ELEMENT_DATA(n) \ + }, \ + }, + +/* data table */ +static struct fwk_element pl011_element_table[] = { + // Macro for array elements + DT_FOREACH_OKAY_INST_arm_pl011(CFG_PL011_ELEMENT_INIT) + // last null element + [DT_N_INST_arm_pl011_NUM_OKAY] = { 0 }, +}; + + +/* access data table from dynamic element callback */ +#ifdef FWK_MODULE_GEN_DYNAMIC_PL011 +const struct fwk_element *_static_get_element_table_pl011() { + return pl011_element_table; +} +#endif + +/* config structure */ +struct fwk_module_config config_dt_pl011 = { + #ifdef FWK_MODULE_GEN_DYNAMIC_PL011 + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(dyn_get_element_table_pl011), + #else + .elements = FWK_MODULE_STATIC_ELEMENTS_PTR(pl011_element_table), + #endif +}; + +/*struct fwk_module_config config_dt_pl011 = { + .elements = FWK_MODULE_STATIC_ELEMENTS ({ + // Macro for array elements + DT_FOREACH_OKAY_INST_arm_pl011(CFG_PL011_ELEMENT_INIT) + // last null element + [DT_N_INST_arm_pl011_NUM_OKAY] = { 0 }, + }), +}; +*/ + +/* - original declaration in juno pl011 +struct fwk_module_config config_pl011 = { + .elements = FWK_MODULE_STATIC_ELEMENTS({ + [0] = { + .name = "", + .data = + &(struct mod_pl011_element_cfg){ + .reg_base = FPGA_UART1_BASE, + .baud_rate_bps = 115200, + .clock_rate_hz = 24 * FWK_MHZ, + .clock_id = FWK_ID_NONE_INIT, + .pd_id = FWK_ID_ELEMENT_INIT( + FWK_MODULE_IDX_POWER_DOMAIN, + POWER_DOMAIN_IDX_SYSTOP), + }, + }, + + [1] = { 0 }, + }), +}; +*/ diff --git a/product/juno/include/juno_dt_bindings.h b/product/juno/include/juno_dt_bindings.h new file mode 100644 index 0000000000000000000000000000000000000000..fd313d5a141b4b125337ae8beb1da400f38d1d75 --- /dev/null +++ b/product/juno/include/juno_dt_bindings.h @@ -0,0 +1,33 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2019-2021, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef JUNO_DT_BINDINGS_H +#define JUNO_DT_BINDINGS_H + +#include "juno_mmap.h" + +/* Level 0 */ +#define POWER_DOMAIN_IDX_BIG_CPU0 0 +#define POWER_DOMAIN_IDX_BIG_CPU1 1 +#define POWER_DOMAIN_IDX_LITTLE_CPU0 2 +#define POWER_DOMAIN_IDX_LITTLE_CPU1 3 +#define POWER_DOMAIN_IDX_LITTLE_CPU2 4 +#define POWER_DOMAIN_IDX_LITTLE_CPU3 5 + + /* Level 1 */ +#define POWER_DOMAIN_IDX_BIG_SSTOP 6 +#define POWER_DOMAIN_IDX_LITTLE_SSTOP 7 +#define POWER_DOMAIN_IDX_DBGSYS 8 +#define POWER_DOMAIN_IDX_GPUTOP 9 + + /* Level 2 */ +#define POWER_DOMAIN_IDX_SYSTOP 10 + +#define POWER_DOMAIN_IDX_NONE -1 + + +#endif /* JUNO_DT_BINDINGS_H */ diff --git a/product/juno/include/juno_mmap.h b/product/juno/include/juno_mmap.h index 59911a85868c46cff3617384500db98dbb152d0e..458655b2540964afe1e272e7e2726b3b1288be2a 100644 --- a/product/juno/include/juno_mmap.h +++ b/product/juno/include/juno_mmap.h @@ -13,8 +13,6 @@ #include "system_mmap.h" -#include - #define I2C_BASE (VENDOR_EXTENSION_BASE + 0x00000000) #define PVT_AON_BASE (VENDOR_EXTENSION_BASE + 0x00001000) #define SCC_BASE (VENDOR_EXTENSION_BASE + 0x1FEFF000) diff --git a/product/juno/include/system_mmap.h b/product/juno/include/system_mmap.h index 80524fd1b3201f54728648c9160c68d4e6812834..4e455d5af4b3da68de17ef629acc62ea9c6f003f 100644 --- a/product/juno/include/system_mmap.h +++ b/product/juno/include/system_mmap.h @@ -11,7 +11,11 @@ #ifndef SYSTEM_MMAP_H #define SYSTEM_MMAP_H +#ifndef DTS_COMPILE #include +#else +#define UINT32_C(n) n +#endif #define PERIPHERAL_BASE UINT32_C(0x44000000) #define EXTERNAL_RAM_BASE UINT32_C(0x60000000) diff --git a/product/juno/scp_ramfw/CMakeLists.txt b/product/juno/scp_ramfw/CMakeLists.txt index 7276343936fb655b30ca8c94b07d586b22ec3e03..1dd60712e09a2d1251bdea0c0cf2194655b08702 100644 --- a/product/juno/scp_ramfw/CMakeLists.txt +++ b/product/juno/scp_ramfw/CMakeLists.txt @@ -17,10 +17,13 @@ target_include_directories( # cmake-lint: disable=E1122 +# "${CMAKE_CURRENT_SOURCE_DIR}/config_pl011.c" + + target_sources( juno-bl2 PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/config_sds.c" - "${CMAKE_CURRENT_SOURCE_DIR}/config_pl011.c" + "${CMAKE_CURRENT_SOURCE_DIR}/config_dt_juno_pl011.c" "${CMAKE_CURRENT_SOURCE_DIR}/config_juno_soc_clock_ram.c" "${CMAKE_CURRENT_SOURCE_DIR}/config_clock.c" "${CMAKE_CURRENT_SOURCE_DIR}/config_dvfs.c" @@ -121,4 +124,4 @@ if(SCP_ENABLE_STATISTICS) endif() set(SCP_MODULES ${SCP_MODULES} PARENT_SCOPE) -set(SCP_MODULE_PATHS ${SCP_MODULE_PATHS} PARENT_SCOPE) \ No newline at end of file +set(SCP_MODULE_PATHS ${SCP_MODULE_PATHS} PARENT_SCOPE) diff --git a/product/juno/scp_ramfw/Firmware.cmake b/product/juno/scp_ramfw/Firmware.cmake index 06d99869469804eb0eea959d67ea6ff2cca28118..d743dd24570af75d7e2450993bf12b2898b248ee 100644 --- a/product/juno/scp_ramfw/Firmware.cmake +++ b/product/juno/scp_ramfw/Firmware.cmake @@ -34,6 +34,10 @@ set(SCP_ENABLE_IPO_INIT FALSE) set(SCP_ENABLE_PMI_INIT FALSE) +set(SCP_FIRMWARE_DTS_SOURCE "${CMAKE_CURRENT_LIST_DIR}/../src/juno-scp.dts") + +list(PREPEND SCP_FIRMWARE_DTS_INCLUDE "${CMAKE_CURRENT_LIST_DIR}/../include") + list(PREPEND SCP_MODULE_PATHS "${CMAKE_CURRENT_LIST_DIR}/../module/juno_reset_domain") list(PREPEND SCP_MODULE_PATHS @@ -105,3 +109,8 @@ list(APPEND SCP_MODULES "mock-psu") list(APPEND SCP_MODULES "juno-pvt") list(APPEND SCP_MODULES "juno-thermal") list(APPEND SCP_MODULES "mock-clock") + +# select device tree configured modules +# These use the .dts configured variants +#list(APPEND SCP_DT_CONFIG_MODULES_STD "clock") +list(APPEND SCP_DT_CONFIG_MODULES_DYN "pl011") \ No newline at end of file diff --git a/product/juno/scp_ramfw/config_dt_juno_pl011.c b/product/juno/scp_ramfw/config_dt_juno_pl011.c new file mode 100644 index 0000000000000000000000000000000000000000..856e845f65b634f0a3cd9a62e2d0cdee92372c58 --- /dev/null +++ b/product/juno/scp_ramfw/config_dt_juno_pl011.c @@ -0,0 +1,21 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2019-2021, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#include +#include +#include + + +/* example dynamic module config table implementation function */ +#ifdef FWK_MODULE_GEN_DYNAMIC_PL011 +const struct fwk_element *dyn_get_element_table_pl011(fwk_id_t module_id) +{ + /* in this case we are simple going to return the static table */ + return _static_get_element_table_pl011(); +} +#endif diff --git a/product/juno/src/juno-scp.dts b/product/juno/src/juno-scp.dts new file mode 100644 index 0000000000000000000000000000000000000000..66b5857a6414fc5c48e69aae3e343837b6c72ad1 --- /dev/null +++ b/product/juno/src/juno-scp.dts @@ -0,0 +1,15 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "juno-scp-base.dtsi" + +/ { + model = "Juno Development Platform (r0-r2)"; + compatible = "arm,juno"; + + +}; \ No newline at end of file diff --git a/tools/dts/README.txt b/tools/dts/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..65fd39c4463cdb3fef642bc712857b36432820de --- /dev/null +++ b/tools/dts/README.txt @@ -0,0 +1,10 @@ +Device tree scripts +------------------- + +These device tree scripts are at present copied over from the Zephyr project, +in order to add DT capabilities to the SCP build process. +(zephyr/scripts/dts)/ + +The libraries are at some point going to become standalone (see README_from_Zephyr.txt), +at which point this project can grab them from the new repository, or +come up with a better process for taking them from the Zephyr project. diff --git a/tools/dts/dt_gen_config.py b/tools/dts/dt_gen_config.py new file mode 100755 index 0000000000000000000000000000000000000000..f24d0176b26014676134303781f167a2ddbf616b --- /dev/null +++ b/tools/dts/dt_gen_config.py @@ -0,0 +1,762 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2019 Linaro Limited +# SPDX-License-Identifier: BSD-3-Clause + +# This script takes some code from zephyr gen_defines.py and also +# uses edtlib from the Zephyr projct to generate module +# config code from a devicetree (.dts) file. +# Information from binding files in YAML format is used as well. +# +# Bindings are files that describe devicetree nodes. Devicetree nodes are +# usually mapped to bindings via their 'compatible = "..."' property. +# + +import argparse +from collections import defaultdict +import os +import pathlib +import re +import sys + +# make the local version of the python devicetree module visible +sys.path.append(os.path.join(os.path.dirname(__file__), 'python-devicetree', + 'src')) + +# use the zephyr dts editlib library +from devicetree import edtlib + +# The set of binding types whose values can be iterated over with +# DT_FOREACH_PROP_ELEM(). If you change this, make sure to update the +# doxygen string for that macro. +FOREACH_PROP_ELEM_TYPES = set(['string', 'array', 'uint8-array', 'string-array', + 'phandles', 'phandle-array']) + +## +## main processing loop +## + +def main(): + global header_file + global scp_base_dir + + scp_base_dir = None + + args = parse_input() + if args.verbose: + print("Generating code from device tree files") + print("using:dts in; ", args.dts) + print(" :dts out; ", args.dts_out) + print(" :header out; ", args.header_out) + print(" :bindings dirs; ", args.bindings_dirs) + print(" :edt pickle out; ", args.edt_pickle_out) + print(" :vendor prefix; ", args.vendor_prefixes) + print(" :dtc flags; ", args.dtc_flags) + print(" :scp_base_dir; ",args.scp_base_dir) + print(" sys.path=", sys.path) + + if args.vendor_prefixes: + vendor_prefixes = edtlib.load_vendor_prefixes_txt(args.vendor_prefixes) + else: + vendor_prefixes = None + + if args.dtc_flags: + warn_reg_addr = "-Wno-simple_bus_reg" not in args.dtc_flags + else: + warn_reg_addr = True + + + if args.scp_base_dir: + scp_base_dir = args.scp_base_dir + + try: + edt = edtlib.EDT(args.dts, args.bindings_dirs, + # Suppress this warning if it's suppressed in dtc + warn_reg_unit_address_mismatch=warn_reg_addr, + default_prop_types=True, + infer_binding_for_paths=["/zephyr,user"], + werror=args.edtlib_Werror, + vendor_prefixes=vendor_prefixes) + except edtlib.EDTError as e: + sys.exit(f"devicetree error: {e}") + + flash_area_num = 0 + + # Save merged DTS source, as a debugging aid + with open(args.dts_out, "w", encoding="utf-8") as f: + print(edt.dts_source, file=f) + + # The raw index into edt.compat2nodes[compat] is used for node + # instance numbering within a compatible. + # + # As a way to satisfy people's intuitions about instance numbers, + # though, we sort this list so enabled instances come first. + # + # This might look like a hack, but it keeps drivers and + # applications which don't use instance numbers carefully working + # as expected, since e.g. instance number 0 is always the + # singleton instance if there's just one enabled node of a + # particular compatible. + # + # This doesn't violate any devicetree.h API guarantees about + # instance ordering, since we make no promises that instance + # numbers are stable across builds. + for compat, nodes in edt.compat2nodes.items(): + edt.compat2nodes[compat] = sorted( + nodes, key=lambda node: 0 if node.status == "okay" else 1) + + # Create the generated header. + with open(args.header_out, "w", encoding="utf-8") as header_file: + write_top_comment(edt) + + # populate all z_path_id first so any children references will + # work correctly. + for node in sorted(edt.nodes, key=lambda node: node.dep_ordinal): + node.z_path_id = node_z_path_id(node) + + for node in sorted(edt.nodes, key=lambda node: node.dep_ordinal): + write_node_comment(node) + + out_comment("Node's full path:") + out_dt_define(f"{node.z_path_id}_PATH", f'"{escape(node.path)}"') + + out_comment("Node's name with unit-address:") + out_dt_define(f"{node.z_path_id}_FULL_NAME", + f'"{escape(node.name)}"') + + if node.parent is not None: + out_comment(f"Node parent ({node.parent.path}) identifier:") + out_dt_define(f"{node.z_path_id}_PARENT", + f"DT_{node.parent.z_path_id}") + + write_child_functions(node) + write_child_functions_status_okay(node) + write_dep_info(node) + write_idents_and_existence(node) + write_bus(node) + write_special_props(node) + write_vanilla_props(node) + + write_chosen(edt) + write_global_compat_info(edt) + extern_header = args.header_out + ".extern" + write_device_extern_header(extern_header, edt) + +## +## write (node) functions +## + +def write_bus(node): + # Macros about the node's bus controller, if there is one + + bus = node.bus_node + if not bus: + return + + if not bus.label: + err(f"missing 'label' property on bus node {bus!r}") + + out_comment(f"Bus info (controller: '{bus.path}', type: '{node.on_bus}')") + out_dt_define(f"{node.z_path_id}_BUS_{str2ident(node.on_bus)}", 1) + out_dt_define(f"{node.z_path_id}_BUS", f"DT_{bus.z_path_id}") + + +def write_special_props(node): + # Writes required macros for special case properties, when the + # data cannot otherwise be obtained from write_vanilla_props() + # results + + global flash_area_num + + out_comment("Special property macros:") + + # Macros that are special to the devicetree specification + write_regs(node) +# write_interrupts(node) + write_compatibles(node) +# write_status(node) + +# if node.parent and "fixed-partitions" in node.parent.compats: +# macro = f"{node.z_path_id}_PARTITION_ID" +# out_dt_define(macro, flash_area_num) +# flash_area_num += 1 + +def write_vanilla_props(node): + # Writes macros for any and all properties defined in the + # "properties" section of the binding for the node. + # + # This does generate macros for special properties as well, like + # regs, etc. Just let that be rather than bothering to add + # never-ending amounts of special case code here to skip special + # properties. This function's macros can't conflict with + # write_special_props() macros, because they're in different + # namespaces. Special cases aren't special enough to break the rules. + + macro2val = {} + for prop_name, prop in node.props.items(): + prop_id = str2ident(prop_name) + macro = f"{node.z_path_id}_P_{prop_id}" + val = prop2value(prop) + if val is not None: + # DT_N__P_ + macro2val[macro] = val + + if prop.spec.type == 'string': + macro2val[macro + "_STRING_TOKEN"] = prop.val_as_token + macro2val[macro + "_STRING_UPPER_TOKEN"] = prop.val_as_token.upper() + + if prop.enum_index is not None: + # DT_N__P__ENUM_IDX + macro2val[macro + "_ENUM_IDX"] = prop.enum_index + spec = prop.spec + + if spec.enum_tokenizable: + as_token = prop.val_as_token + + # DT_N__P__ENUM_TOKEN + macro2val[macro + "_ENUM_TOKEN"] = as_token + + if spec.enum_upper_tokenizable: + # DT_N__P__ENUM_UPPER_TOKEN + macro2val[macro + "_ENUM_UPPER_TOKEN"] = as_token.upper() + + if "phandle" in prop.type: + macro2val.update(phandle_macros(prop, macro)) + elif "array" in prop.type: + # DT_N__P__IDX_ + # DT_N__P__IDX__EXISTS + for i, subval in enumerate(prop.val): + if isinstance(subval, str): + macro2val[macro + f"_IDX_{i}"] = quote_str(subval) + else: + macro2val[macro + f"_IDX_{i}"] = subval + macro2val[macro + f"_IDX_{i}_EXISTS"] = 1 + + if prop.type in FOREACH_PROP_ELEM_TYPES: + # DT_N__P__FOREACH_PROP_ELEM + macro2val[f"{macro}_FOREACH_PROP_ELEM(fn)"] = \ + ' \\\n\t'.join(f'fn(DT_{node.z_path_id}, {prop_id}, {i})' + for i in range(len(prop.val))) + + macro2val[f"{macro}_FOREACH_PROP_ELEM_VARGS(fn, ...)"] = \ + ' \\\n\t'.join(f'fn(DT_{node.z_path_id}, {prop_id}, {i},' + ' __VA_ARGS__)' + for i in range(len(prop.val))) + + plen = prop_len(prop) + if plen is not None: + # DT_N__P__LEN + macro2val[macro + "_LEN"] = plen + + macro2val[f"{macro}_EXISTS"] = 1 + + if macro2val: + out_comment("Generic property macros:") + for macro, val in macro2val.items(): + out_dt_define(macro, val) + else: + out_comment("(No generic property macros)") + + + +def write_regs(node): + # reg property: edtlib knows the right #address-cells and + # #size-cells, and can therefore pack the register base addresses + # and sizes correctly + + idx_vals = [] + name_vals = [] + path_id = node.z_path_id + + if node.regs is not None: + idx_vals.append((f"{path_id}_REG_NUM", len(node.regs))) + + for i, reg in enumerate(node.regs): + idx_vals.append((f"{path_id}_REG_IDX_{i}_EXISTS", 1)) + if reg.addr is not None: + idx_macro = f"{path_id}_REG_IDX_{i}_VAL_ADDRESS" + idx_vals.append((idx_macro, + f"{reg.addr} /* {hex(reg.addr)} */")) + if reg.name: + name_macro = f"{path_id}_REG_NAME_{reg.name}_VAL_ADDRESS" + name_vals.append((name_macro, f"DT_{idx_macro}")) + + if reg.size is not None: + idx_macro = f"{path_id}_REG_IDX_{i}_VAL_SIZE" + idx_vals.append((idx_macro, + f"{reg.size} /* {hex(reg.size)} */")) + if reg.name: + name_macro = f"{path_id}_REG_NAME_{reg.name}_VAL_SIZE" + name_vals.append((name_macro, f"DT_{idx_macro}")) + + for macro, val in idx_vals: + out_dt_define(macro, val) + for macro, val in name_vals: + out_dt_define(macro, val) + +def write_compatibles(node): + # Writes a macro for each of the node's compatibles. We don't care + # about whether edtlib / Zephyr's binding language recognizes + # them. The compatibles the node provides are what is important. + + for compat in node.compats: + out_dt_define( + f"{node.z_path_id}_COMPAT_MATCHES_{str2ident(compat)}", 1) + +def write_node_comment(node): + # Writes a comment describing 'node' to the header and configuration file + + s = f"""\ +Devicetree node: {node.path} + +Node identifier: DT_{node.z_path_id} +""" + + if node.matching_compat: + if node.binding_path: + s += f""" +Binding (compatible = {node.matching_compat}): + {relativize(node.binding_path)} +""" + else: + s += f""" +Binding (compatible = {node.matching_compat}): + No yaml (bindings inferred from properties) +""" + + if node.description: + # We used to put descriptions in the generated file, but + # devicetree bindings now have pages in the HTML + # documentation. Let users who are accustomed to digging + # around in the generated file where to find the descriptions + # now. + # + # Keeping them here would mean that the descriptions + # themselves couldn't contain C multi-line comments, which is + # inconvenient when we want to do things like quote snippets + # of .dtsi files within the descriptions, or otherwise + # include the string "*/". + s += ("\n(Descriptions have moved to the Devicetree Bindings Index\n" + "in the documentation.)\n") + + out_comment(s) + +def write_child_functions(node): + # Writes macro that are helpers that will call a macro/function + # for each child node. + + out_dt_define(f"{node.z_path_id}_FOREACH_CHILD(fn)", + " ".join(f"fn(DT_{child.z_path_id})" for child in + node.children.values())) + + out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_VARGS(fn, ...)", + " ".join(f"fn(DT_{child.z_path_id}, __VA_ARGS__)" for child in + node.children.values())) + +def write_child_functions_status_okay(node): + # Writes macro that are helpers that will call a macro/function + # for each child node with status "okay". + + functions = '' + functions_args = '' + for child in node.children.values(): + if child.status == "okay": + functions = functions + f"fn(DT_{child.z_path_id}) " + functions_args = functions_args + f"fn(DT_{child.z_path_id}, " \ + "__VA_ARGS__) " + + out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY(fn)", functions) + out_dt_define(f"{node.z_path_id}_FOREACH_CHILD_STATUS_OKAY_VARGS(fn, ...)", + functions_args) + + +def write_idents_and_existence(node): + # Writes macros related to the node's aliases, labels, etc., + # as well as existence flags. + + # Aliases + idents = [f"N_ALIAS_{str2ident(alias)}" for alias in node.aliases] + # Instances + for compat in node.compats: + instance_no = node.edt.compat2nodes[compat].index(node) + idents.append(f"N_INST_{instance_no}_{str2ident(compat)}") + # Node labels + idents.extend(f"N_NODELABEL_{str2ident(label)}" for label in node.labels) + + out_comment("Existence and alternate IDs:") + out_dt_define(node.z_path_id + "_EXISTS", 1) + + # Only determine maxlen if we have any idents + if idents: + maxlen = max(len("DT_" + ident) for ident in idents) + for ident in idents: + out_dt_define(ident, "DT_" + node.z_path_id, width=maxlen) + +def write_dep_info(node): + # Write dependency-related information about the node. + + def fmt_dep_list(dep_list): + if dep_list: + # Sort the list by dependency ordinal for predictability. + sorted_list = sorted(dep_list, key=lambda node: node.dep_ordinal) + return "\\\n\t" + \ + " \\\n\t".join(f"{n.dep_ordinal}, /* {n.path} */" + for n in sorted_list) + else: + return "/* nothing */" + + out_comment("Node's dependency ordinal:") + out_dt_define(f"{node.z_path_id}_ORD", node.dep_ordinal) + + out_comment("Ordinals for what this node depends on directly:") + out_dt_define(f"{node.z_path_id}_REQUIRES_ORDS", + fmt_dep_list(node.depends_on)) + + out_comment("Ordinals for what depends directly on this node:") + out_dt_define(f"{node.z_path_id}_SUPPORTS_ORDS", + fmt_dep_list(node.required_by)) + +## +## write (edt) functions +## + +def write_chosen(edt): + # Tree-wide information such as chosen nodes is printed here. + + out_comment("Chosen nodes\n") + chosen = {} + for name, node in edt.chosen_nodes.items(): + chosen[f"DT_CHOSEN_{str2ident(name)}"] = f"DT_{node.z_path_id}" + chosen[f"DT_CHOSEN_{str2ident(name)}_EXISTS"] = 1 + max_len = max(map(len, chosen), default=0) + for macro, value in chosen.items(): + out_define(macro, value, width=max_len) + + +def write_global_compat_info(edt): + # Tree-wide information related to each compatible, such as number + # of instances with status "okay", is printed here. + + n_okay_macros = {} + for_each_macros = {} + compat2buses = defaultdict(list) # just for "okay" nodes + for compat, okay_nodes in edt.compat2okay.items(): + for node in okay_nodes: + bus = node.on_bus + if bus is not None and bus not in compat2buses[compat]: + compat2buses[compat].append(bus) + + ident = str2ident(compat) + n_okay_macros[f"DT_N_INST_{ident}_NUM_OKAY"] = len(okay_nodes) + + # Helpers for non-INST for-each macros that take node + # identifiers as arguments. + for_each_macros[f"DT_FOREACH_OKAY_{ident}(fn)"] = \ + " ".join(f"fn(DT_{node.z_path_id})" + for node in okay_nodes) + for_each_macros[f"DT_FOREACH_OKAY_VARGS_{ident}(fn, ...)"] = \ + " ".join(f"fn(DT_{node.z_path_id}, __VA_ARGS__)" + for node in okay_nodes) + + # Helpers for INST versions of for-each macros, which take + # instance numbers. We emit separate helpers for these because + # avoiding an intermediate node_id --> instance number + # conversion in the preprocessor helps to keep the macro + # expansions simpler. That hopefully eases debugging. + for_each_macros[f"DT_FOREACH_OKAY_INST_{ident}(fn)"] = \ + " ".join(f"fn({edt.compat2nodes[compat].index(node)})" + for node in okay_nodes) + for_each_macros[f"DT_FOREACH_OKAY_INST_VARGS_{ident}(fn, ...)"] = \ + " ".join(f"fn({edt.compat2nodes[compat].index(node)}, __VA_ARGS__)" + for node in okay_nodes) + + for compat, nodes in edt.compat2nodes.items(): + for node in nodes: + if compat == "fixed-partitions": + for child in node.children.values(): + if "label" in child.props: + label = child.props["label"].val + macro = f"COMPAT_{str2ident(compat)}_LABEL_{str2ident(label)}" + val = f"DT_{child.z_path_id}" + + out_dt_define(macro, val) + out_dt_define(macro + "_EXISTS", 1) + + out_comment('Macros for compatibles with status "okay" nodes\n') + for compat, okay_nodes in edt.compat2okay.items(): + if okay_nodes: + out_define(f"DT_COMPAT_HAS_OKAY_{str2ident(compat)}", 1) + + out_comment('Macros for status "okay" instances of each compatible\n') + for macro, value in n_okay_macros.items(): + out_define(macro, value) + for macro, value in for_each_macros.items(): + out_define(macro, value) + + out_comment('Bus information for status "okay" nodes of each compatible\n') + for compat, buses in compat2buses.items(): + for bus in buses: + out_define( + f"DT_COMPAT_{str2ident(compat)}_BUS_{str2ident(bus)}", 1) + + +def write_top_comment(edt): + # Writes an overview comment with misc. info at the top of the header and + # configuration file + + s = f"""\ +Generated by gen_defines.py + +DTS input file: + {edt.dts_path} + +Directories with bindings: + {", ".join(map(relativize, edt.bindings_dirs))} + +Node dependency ordering (ordinal and path): +""" + + for scc in edt.scc_order: + if len(scc) > 1: + err("cycle in devicetree involving " + + ", ".join(node.path for node in scc)) + s += f" {scc[0].dep_ordinal:<3} {scc[0].path}\n" + + s += """ +Definitions derived from these nodes in dependency order are next, +followed by /chosen nodes. +""" + + out_comment(s, blank_before=False) + + ### not really used for SP but look to see what it does. +def write_device_extern_header(device_header_out, edt): + # Generate header that will extern devicetree struct device's + + with open(device_header_out, "w", encoding="utf-8") as dev_header_file: + print("#ifndef DEVICE_EXTERN_GEN_H", file=dev_header_file) + print("#define DEVICE_EXTERN_GEN_H", file=dev_header_file) + print("", file=dev_header_file) + print("#ifdef __cplusplus", file=dev_header_file) + print('extern "C" {', file=dev_header_file) + print("#endif", file=dev_header_file) + print("", file=dev_header_file) + + for node in sorted(edt.nodes, key=lambda node: node.dep_ordinal): + print(f"extern const struct device DEVICE_DT_NAME_GET(DT_{node.z_path_id}); /* dts_ord_{node.dep_ordinal} */", + file=dev_header_file) + + print("", file=dev_header_file) + print("#ifdef __cplusplus", file=dev_header_file) + print("}", file=dev_header_file) + print("#endif", file=dev_header_file) + print("", file=dev_header_file) + print("#endif /* DEVICE_EXTERN_GEN_H */", file=dev_header_file) + + + + +## +## out_ string functions +## + +def out_dt_define(macro, val, width=None, deprecation_msg=None): + # Writes "#define DT_ " to the header file + # + # The macro will be left-justified to 'width' characters if that + # is specified, and the value will follow immediately after in + # that case. Otherwise, this function decides how to add + # whitespace between 'macro' and 'val'. + # + # If a 'deprecation_msg' string is passed, the generated identifiers will + # generate a warning if used, via __WARN()). + # + # Returns the full generated macro for 'macro', with leading "DT_". + ret = "DT_" + macro + out_define(ret, val, width=width, deprecation_msg=deprecation_msg) + return ret + + +def out_define(macro, val, width=None, deprecation_msg=None): + # Helper for out_dt_define(). Outputs "#define ", + # adds a deprecation message if given, and allocates whitespace + # unless told not to. + + warn = fr' __WARN("{deprecation_msg}")' if deprecation_msg else "" + + if width: + s = f"#define {macro.ljust(width)}{warn} {val}" + else: + s = f"#define {macro}{warn} {val}" + + print(s, file=header_file) + +def out_comment(s, blank_before=True): + # Writes 's' as a comment to the header and configuration file. 's' is + # allowed to have multiple lines. blank_before=True adds a blank line + # before the comment. + + if blank_before: + print(file=header_file) + + if "\n" in s: + # Format multi-line comments like + # + # /* + # * first line + # * second line + # * + # * empty line before this line + # */ + res = ["/*"] + for line in s.splitlines(): + # Avoid an extra space after '*' for empty lines. They turn red in + # Vim if space error checking is on, which is annoying. + res.append(" *" if not line.strip() else " * " + line) + res.append(" */") + print("\n".join(res), file=header_file) + else: + # Format single-line comments like + # + # /* foo bar */ + print("/* " + s + " */", file=header_file) + +## +## general utility functions +## + +def relativize(path): + # If 'path' is within scp_base_dir, returns it relative to scp_base_dir, + # with a "$ZEPHYR_BASE/..." hint at the start of the string. Otherwise, + # returns 'path' unchanged. + + if scp_base_dir is None: + return path + + try: + return str("SCP_BASE_DIR" / pathlib.Path(path).relative_to(scp_base_dir)) + except ValueError: + # Not within ZEPHYR_BASE + return path + +def quote_str(s): + # Puts quotes around 's' and escapes any double quotes and + # backslashes within it + + return f'"{escape(s)}"' +def node_z_path_id(node): + # Return the node specific bit of the node's path identifier: + # + # - the root node's path "/" has path identifier "N" + # - "/foo" has "N_S_foo" + # - "/foo/bar" has "N_S_foo_S_bar" + # - "/foo/bar@123" has "N_S_foo_S_bar_123" + # + # This is used throughout this file to generate macros related to + # the node. + + components = ["N"] + if node.parent is not None: + components.extend(f"S_{str2ident(component)}" for component in + node.path.split("/")[1:]) + + return "_".join(components) + +def str2ident(s): + # Converts 's' to a form suitable for (part of) an identifier + + return re.sub('[-,.@/+]', '_', s.lower()) + +def escape(s): + # Backslash-escapes any double quotes and backslashes in 's' + + # \ must be escaped before " to avoid double escaping + return s.replace("\\", "\\\\").replace('"', '\\"') + +def prop2value(prop): + # Gets the macro value for property 'prop', if there is + # a single well-defined C rvalue that it can be represented as. + # Returns None if there isn't one. + + if prop.type == "string": + return quote_str(prop.val) + + if prop.type == "int": + return prop.val + + if prop.type == "boolean": + return 1 if prop.val else 0 + + if prop.type in ["array", "uint8-array"]: + return list2init(f"{val} /* {hex(val)} */" for val in prop.val) + + if prop.type == "string-array": + return list2init(quote_str(val) for val in prop.val) + + # phandle, phandles, phandle-array, path, compound: nothing + return None + +def prop_len(prop): + # Returns the property's length if and only if we should generate + # a _LEN macro for the property. Otherwise, returns None. + # + # This deliberately excludes reg and interrupts. + # While they have array type, their lengths as arrays are + # basically nonsense semantically due to #address-cells and + # #size-cells for "reg" and #interrupt-cells for "interrupts". + # + # We have special purpose macros for the number of register blocks + # / interrupt specifiers. Excluding them from this list means + # DT_PROP_LEN(node_id, ...) fails fast at the devicetree.h layer + # with a build error. This forces users to switch to the right + # macros. + + if prop.type == "phandle": + return 1 + + if (prop.type in ["array", "uint8-array", "string-array", + "phandles", "phandle-array"] and + prop.name not in ["reg", "interrupts"]): + return len(prop.val) + + return None + +def list2init(l): + # Converts 'l', a Python list (or iterable), to a C array initializer + + return "{" + ", ".join(l) + "}" + +## +## input options +## + +def parse_input(): + # parse the command line arguments + parser = argparse.ArgumentParser(); + parser.add_argument("--dts", required=True, help ="The input .dts file") + parser.add_argument("--bindings-dirs", nargs='+', required=True, + help="directory with bindings in YAML format, " + "we allow multiple") + parser.add_argument("--header-out", required=True, + help="path and filename to write header to") + parser.add_argument("--dts-out", required=True, + help="path to write merged DTS source code to (e.g. " + "as a debugging aid)") + parser.add_argument("--edt-pickle-out", + help="path to write pickled edtlib.EDT object to") + parser.add_argument("--vendor-prefixes", + help="vendor-prefixes.txt path; used for validation") + parser.add_argument("--dtc-flags", + help="'dtc' devicetree compiler flags, some of which " + "might be respected here") + parser.add_argument("--verbose", help="extra script logging for debug") + parser.add_argument("--edtlib-Werror", action="store_true", + help="if set, edtlib-specific warnings become errors. " + "(this does not apply to warnings shared " + "with dtc.)") + parser.add_argument("--scp-base-dir") + return parser.parse_args() + +# put in the main guard +if __name__ == "__main__": + main() diff --git a/tools/dts/python-devicetree/.gitignore b/tools/dts/python-devicetree/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..20574a34569ec420ec253a9d71e31f075b06f55e --- /dev/null +++ b/tools/dts/python-devicetree/.gitignore @@ -0,0 +1,7 @@ +dist/ +src/devicetree.egg-info/ +build/ +devicetree.egg-info/ +__pycache__/ +.tox/ +doc/build/ diff --git a/tools/dts/python-devicetree/requirements.txt b/tools/dts/python-devicetree/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..f827ad8d1d68a48da5427961cafb473c45241bcd --- /dev/null +++ b/tools/dts/python-devicetree/requirements.txt @@ -0,0 +1 @@ +sphinx_rtd_theme # docs diff --git a/tools/dts/python-devicetree/setup.py b/tools/dts/python-devicetree/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..590ba3151048d87c92213dddbd21a90bed0ace1e --- /dev/null +++ b/tools/dts/python-devicetree/setup.py @@ -0,0 +1,42 @@ +# Copyright (c) 2021, Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +import setuptools + +long_description = ''' +Placeholder +=========== + +This is just a placeholder for moving Zephyr's devicetree libraries +to PyPI. +''' + +version = '0.0.1' + +setuptools.setup( + # TBD, just use these for now. + author='Zephyr Project', + author_email='devel@lists.zephyrproject.org', + + name='devicetree', + version=version, + description='Python libraries for devicetree', + long_description=long_description, + # http://docutils.sourceforge.net/FAQ.html#what-s-the-official-mime-type-for-restructuredtext-data + long_description_content_type="text/x-rst", + url='https://github.com/zephyrproject-rtos/python-devicetree', + packages=setuptools.find_packages(where='src'), + package_dir={'': 'src'}, + classifiers=[ + 'Programming Language :: Python :: 3 :: Only', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows', + ], + install_requires=[ + 'PyYAML>=5.1', + ], + python_requires='>=3.6', +) diff --git a/tools/dts/python-devicetree/src/devicetree/__init__.py b/tools/dts/python-devicetree/src/devicetree/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e9a568330b68a521836a79cdc15b1fc369acfa37 --- /dev/null +++ b/tools/dts/python-devicetree/src/devicetree/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) 2021 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +__all__ = ['edtlib', 'dtlib'] diff --git a/tools/dts/python-devicetree/src/devicetree/dtlib.py b/tools/dts/python-devicetree/src/devicetree/dtlib.py new file mode 100644 index 0000000000000000000000000000000000000000..9d5f32cf711683c2b249faf41b18a2964f69ba4c --- /dev/null +++ b/tools/dts/python-devicetree/src/devicetree/dtlib.py @@ -0,0 +1,2026 @@ +# Copyright (c) 2019, Nordic Semiconductor +# SPDX-License-Identifier: BSD-3-Clause + +# Tip: You can view just the documentation with 'pydoc3 devicetree.dtlib' + +# _init_tokens() builds names dynamically. +# +# pylint: disable=undefined-variable + +""" +A library for extracting information from .dts (devicetree) files. See the +documentation for the DT and Node classes for more information. + +The top-level entry point of the library is the DT class. DT.__init__() takes a +.dts file to parse and a list of directories to search for any /include/d +files. +""" + +import collections +import enum +import errno +import os +import re +import sys +import textwrap +from typing import Any, Dict, Iterable, List, \ + NamedTuple, NoReturn, Optional, Tuple, Union + +# NOTE: tests/test_dtlib.py is the test suite for this library. + +class DTError(Exception): + "Exception raised for devicetree-related errors" + +class Node: + r""" + Represents a node in the devicetree ('node-name { ... };'). + + These attributes are available on Node instances: + + name: + The name of the node (a string). + + unit_addr: + The portion after the '@' in the node's name, or the empty string if the + name has no '@' in it. + + Note that this is a string. Run int(node.unit_addr, 16) to get an + integer. + + props: + A collections.OrderedDict that maps the properties defined on the node to + their values. 'props' is indexed by property name (a string), and values + are Property objects. + + To convert property values to Python numbers or strings, use + dtlib.to_num(), dtlib.to_nums(), or dtlib.to_string(). + + Property values are represented as 'bytes' arrays to support the full + generality of DTS, which allows assignments like + + x = "foo", < 0x12345678 >, [ 9A ]; + + This gives x the value b"foo\0\x12\x34\x56\x78\x9A". Numbers in DTS are + stored in big-endian format. + + nodes: + A collections.OrderedDict containing the subnodes of the node, indexed by + name. + + labels: + A list with all labels pointing to the node, in the same order as the + labels appear, but with duplicates removed. + + 'label_1: label_2: node { ... };' gives 'labels' the value + ["label_1", "label_2"]. + + parent: + The parent Node of the node. 'None' for the root node. + + path: + The path to the node as a string, e.g. "/foo/bar". + + dt: + The DT instance this node belongs to. + """ + + # + # Public interface + # + + def __init__(self, name: str, parent: Optional['Node'], dt: 'DT'): + """ + Node constructor. Not meant to be called directly by clients. + """ + self.name = name + self.parent = parent + self.dt = dt + + self.props: Dict[str, 'Property'] = collections.OrderedDict() + self.nodes: Dict[str, 'Node'] = collections.OrderedDict() + self.labels: List[str] = [] + self._omit_if_no_ref = False + self._is_referenced = False + + @property + def unit_addr(self) -> str: + """ + See the class documentation. + """ + return self.name.partition("@")[2] + + @property + def path(self) -> str: + """ + See the class documentation. + """ + node_names = [] + + cur = self + while cur.parent: + node_names.append(cur.name) + cur = cur.parent + + return "/" + "/".join(reversed(node_names)) + + def node_iter(self) -> Iterable['Node']: + """ + Returns a generator for iterating over the node and its children, + recursively. + + For example, this will iterate over all nodes in the tree (like + dt.node_iter()). + + for node in dt.root.node_iter(): + ... + """ + yield self + for node in self.nodes.values(): + yield from node.node_iter() + + def _get_prop(self, name: str) -> 'Property': + # Returns the property named 'name' on the node, creating it if it + # doesn't already exist + + prop = self.props.get(name) + if not prop: + prop = Property(self, name) + self.props[name] = prop + return prop + + def _del(self) -> None: + # Removes the node from the tree + self.parent.nodes.pop(self.name) # type: ignore + + def __str__(self): + """ + Returns a DTS representation of the node. Called automatically if the + node is print()ed. + """ + s = "".join(label + ": " for label in self.labels) + + s += "{} {{\n".format(self.name) + + for prop in self.props.values(): + s += "\t" + str(prop) + "\n" + + for child in self.nodes.values(): + s += textwrap.indent(child.__str__(), "\t") + "\n" + + s += "};" + + return s + + def __repr__(self): + """ + Returns some information about the Node instance. Called automatically + if the Node instance is evaluated. + """ + return "" \ + .format(self.path, self.dt.filename) + +# See Property.type +class Type(enum.IntEnum): + EMPTY = 0 + BYTES = 1 + NUM = 2 + NUMS = 3 + STRING = 4 + STRINGS = 5 + PATH = 6 + PHANDLE = 7 + PHANDLES = 8 + PHANDLES_AND_NUMS = 9 + COMPOUND = 10 + +class _MarkerType(enum.IntEnum): + # Types of markers in property values + + # References + PATH = 0 # &foo + PHANDLE = 1 # <&foo> + LABEL = 2 # foo: <1 2 3> + + # Start of data blocks of specific type + UINT8 = 3 # [00 01 02] (and also used for /incbin/) + UINT16 = 4 # /bits/ 16 <1 2 3> + UINT32 = 5 # <1 2 3> + UINT64 = 6 # /bits/ 64 <1 2 3> + STRING = 7 # "foo" + +class Property: + """ + Represents a property ('x = ...'). + + These attributes are available on Property instances: + + name: + The name of the property (a string). + + value: + The value of the property, as a 'bytes' string. Numbers are stored in + big-endian format, and strings are null-terminated. Putting multiple + comma-separated values in an assignment (e.g., 'x = < 1 >, "foo"') will + concatenate the values. + + See the to_*() methods for converting the value to other types. + + type: + The type of the property, inferred from the syntax used in the + assignment. This is one of the following constants (with example + assignments): + + Assignment | Property.type + ----------------------------+------------------------ + foo; | dtlib.Type.EMPTY + foo = []; | dtlib.Type.BYTES + foo = [01 02]; | dtlib.Type.BYTES + foo = /bits/ 8 <1>; | dtlib.Type.BYTES + foo = <1>; | dtlib.Type.NUM + foo = <>; | dtlib.Type.NUMS + foo = <1 2 3>; | dtlib.Type.NUMS + foo = <1 2>, <3>; | dtlib.Type.NUMS + foo = "foo"; | dtlib.Type.STRING + foo = "foo", "bar"; | dtlib.Type.STRINGS + foo = <&l>; | dtlib.Type.PHANDLE + foo = <&l1 &l2 &l3>; | dtlib.Type.PHANDLES + foo = <&l1 &l2>, <&l3>; | dtlib.Type.PHANDLES + foo = <&l1 1 2 &l2 3 4>; | dtlib.Type.PHANDLES_AND_NUMS + foo = <&l1 1 2>, <&l2 3 4>; | dtlib.Type.PHANDLES_AND_NUMS + foo = &l; | dtlib.Type.PATH + *Anything else* | dtlib.Type.COMPOUND + + *Anything else* includes properties mixing phandle (<&label>) and node + path (&label) references with other data. + + Data labels in the property value do not influence the type. + + labels: + A list with all labels pointing to the property, in the same order as the + labels appear, but with duplicates removed. + + 'label_1: label2: x = ...' gives 'labels' the value + {"label_1", "label_2"}. + + offset_labels: + A dictionary that maps any labels within the property's value to their + offset, in bytes. For example, 'x = < 0 label_1: 1 label_2: >' gives + 'offset_labels' the value {"label_1": 4, "label_2": 8}. + + Iteration order will match the order of the labels on Python versions + that preserve dict insertion order. + + node: + The Node the property is on. + """ + + # + # Public interface + # + + def __init__(self, node: Node, name: str): + if "@" in name: + node.dt._parse_error("'@' is only allowed in node names") + + self.name = name + self.node = node + self.value = b"" + self.labels: List[str] = [] + self._label_offset_lst: List[Tuple[str, int]] = [] + # We have to wait to set this until later, when we've got + # the entire tree. + self.offset_labels: Dict[str, int] = {} + + # A list of [offset, label, type] lists (sorted by offset), + # giving the locations of references within the value. 'type' + # is either _MarkerType.PATH, for a node path reference, + # _MarkerType.PHANDLE, for a phandle reference, or + # _MarkerType.LABEL, for a label on/within data. Node paths + # and phandles need to be patched in after parsing. + self._markers: List[List] = [] + + def to_num(self, signed=False) -> int: + """ + Returns the value of the property as a number. + + Raises DTError if the property was not assigned with this syntax (has + Property.type Type.NUM): + + foo = < 1 >; + + signed (default: False): + If True, the value will be interpreted as signed rather than + unsigned. + """ + if self.type is not Type.NUM: + _err("expected property '{0}' on {1} in {2} to be assigned with " + "'{0} = < (number) >;', not '{3}'" + .format(self.name, self.node.path, self.node.dt.filename, + self)) + + return int.from_bytes(self.value, "big", signed=signed) + + def to_nums(self, signed=False) -> List[int]: + """ + Returns the value of the property as a list of numbers. + + Raises DTError if the property was not assigned with this syntax (has + Property.type Type.NUM or Type.NUMS): + + foo = < 1 2 ... >; + + signed (default: False): + If True, the values will be interpreted as signed rather than + unsigned. + """ + if self.type not in (Type.NUM, Type.NUMS): + _err("expected property '{0}' on {1} in {2} to be assigned with " + "'{0} = < (number) (number) ... >;', not '{3}'" + .format(self.name, self.node.path, self.node.dt.filename, + self)) + + return [int.from_bytes(self.value[i:i + 4], "big", signed=signed) + for i in range(0, len(self.value), 4)] + + def to_bytes(self) -> bytes: + """ + Returns the value of the property as a raw 'bytes', like + Property.value, except with added type checking. + + Raises DTError if the property was not assigned with this syntax (has + Property.type Type.BYTES): + + foo = [ 01 ... ]; + """ + if self.type is not Type.BYTES: + _err("expected property '{0}' on {1} in {2} to be assigned with " + "'{0} = [ (byte) (byte) ... ];', not '{3}'" + .format(self.name, self.node.path, self.node.dt.filename, + self)) + + return self.value + + def to_string(self) -> str: + """ + Returns the value of the property as a string. + + Raises DTError if the property was not assigned with this syntax (has + Property.type Type.STRING): + + foo = "string"; + + This function might also raise UnicodeDecodeError if the string is + not valid UTF-8. + """ + if self.type is not Type.STRING: + _err("expected property '{0}' on {1} in {2} to be assigned with " + "'{0} = \"string\";', not '{3}'" + .format(self.name, self.node.path, self.node.dt.filename, + self)) + + try: + ret = self.value.decode("utf-8")[:-1] # Strip null + except UnicodeDecodeError: + _err("value of property '{}' ({!r}) on {} in {} is not valid UTF-8" + .format(self.name, self.value, self.node.path, + self.node.dt.filename)) + + return ret # The separate 'return' appeases the type checker. + + def to_strings(self) -> List[str]: + """ + Returns the value of the property as a list of strings. + + Raises DTError if the property was not assigned with this syntax (has + Property.type Type.STRING or Type.STRINGS): + + foo = "string", "string", ... ; + + Also raises DTError if any of the strings are not valid UTF-8. + """ + if self.type not in (Type.STRING, Type.STRINGS): + _err("expected property '{0}' on {1} in {2} to be assigned with " + "'{0} = \"string\", \"string\", ... ;', not '{3}'" + .format(self.name, self.node.path, self.node.dt.filename, + self)) + + try: + ret = self.value.decode("utf-8").split("\0")[:-1] + except UnicodeDecodeError: + _err("value of property '{}' ({!r}) on {} in {} is not valid UTF-8" + .format(self.name, self.value, self.node.path, + self.node.dt.filename)) + + return ret # The separate 'return' appeases the type checker. + + def to_node(self) -> Node: + """ + Returns the Node the phandle in the property points to. + + Raises DTError if the property was not assigned with this syntax (has + Property.type Type.PHANDLE). + + foo = < &bar >; + """ + if self.type is not Type.PHANDLE: + _err("expected property '{0}' on {1} in {2} to be assigned with " + "'{0} = < &foo >;', not '{3}'" + .format(self.name, self.node.path, self.node.dt.filename, + self)) + + return self.node.dt.phandle2node[int.from_bytes(self.value, "big")] + + def to_nodes(self) -> List[Node]: + """ + Returns a list with the Nodes the phandles in the property point to. + + Raises DTError if the property value contains anything other than + phandles. All of the following are accepted: + + foo = < > + foo = < &bar >; + foo = < &bar &baz ... >; + foo = < &bar ... >, < &baz ... >; + """ + def type_ok(): + if self.type in (Type.PHANDLE, Type.PHANDLES): + return True + # Also accept 'foo = < >;' + return self.type is Type.NUMS and not self.value + + if not type_ok(): + _err("expected property '{0}' on {1} in {2} to be assigned with " + "'{0} = < &foo &bar ... >;', not '{3}'" + .format(self.name, self.node.path, + self.node.dt.filename, self)) + + return [self.node.dt.phandle2node[int.from_bytes(self.value[i:i + 4], + "big")] + for i in range(0, len(self.value), 4)] + + def to_path(self) -> Node: + """ + Returns the Node referenced by the path stored in the property. + + Raises DTError if the property was not assigned with either of these + syntaxes (has Property.type Type.PATH or Type.STRING): + + foo = &bar; + foo = "/bar"; + + For the second case, DTError is raised if the path does not exist. + """ + if self.type not in (Type.PATH, Type.STRING): + _err("expected property '{0}' on {1} in {2} to be assigned with " + "either '{0} = &foo' or '{0} = \"/path/to/node\"', not '{3}'" + .format(self.name, self.node.path, self.node.dt.filename, + self)) + + try: + path = self.value.decode("utf-8")[:-1] + except UnicodeDecodeError: + _err("value of property '{}' ({!r}) on {} in {} is not valid UTF-8" + .format(self.name, self.value, self.node.path, + self.node.dt.filename)) + + try: + ret = self.node.dt.get_node(path) + except DTError: + _err("property '{}' on {} in {} points to the non-existent node " + "\"{}\"".format(self.name, self.node.path, + self.node.dt.filename, path)) + + return ret # The separate 'return' appeases the type checker. + + @property + def type(self) -> int: + """ + See the class docstring. + """ + # Data labels (e.g. 'foo = label: <3>') are irrelevant, so filter them + # out + types = [marker[1] for marker in self._markers + if marker[1] != _MarkerType.LABEL] + + if not types: + return Type.EMPTY + + if types == [_MarkerType.UINT8]: + return Type.BYTES + + if types == [_MarkerType.UINT32]: + return Type.NUM if len(self.value) == 4 else Type.NUMS + + # Treat 'foo = <1 2 3>, <4 5>, ...' as Type.NUMS too + if set(types) == {_MarkerType.UINT32}: + return Type.NUMS + + if set(types) == {_MarkerType.STRING}: + return Type.STRING if len(types) == 1 else Type.STRINGS + + if types == [_MarkerType.PATH]: + return Type.PATH + + if types == [_MarkerType.UINT32, _MarkerType.PHANDLE] and \ + len(self.value) == 4: + return Type.PHANDLE + + if set(types) == {_MarkerType.UINT32, _MarkerType.PHANDLE}: + if len(self.value) == 4*types.count(_MarkerType.PHANDLE): + # Array with just phandles in it + return Type.PHANDLES + # Array with both phandles and numbers + return Type.PHANDLES_AND_NUMS + + return Type.COMPOUND + + def __str__(self): + s = "".join(label + ": " for label in self.labels) + self.name + if not self.value: + return s + ";" + + s += " =" + + for i, (pos, marker_type, ref) in enumerate(self._markers): + if i < len(self._markers) - 1: + next_marker = self._markers[i + 1] + else: + next_marker = None + + # End of current marker + end = next_marker[0] if next_marker else len(self.value) + + if marker_type is _MarkerType.STRING: + # end - 1 to strip off the null terminator + s += ' "{}"'.format(_decode_and_escape( + self.value[pos:end - 1])) + if end != len(self.value): + s += "," + elif marker_type is _MarkerType.PATH: + s += " &" + ref + if end != len(self.value): + s += "," + else: + # <> or [] + + if marker_type is _MarkerType.LABEL: + s += " {}:".format(ref) + elif marker_type is _MarkerType.PHANDLE: + s += " &" + ref + pos += 4 + # Subtle: There might be more data between the phandle and + # the next marker, so we can't 'continue' here + else: # marker_type is _MarkerType.UINT* + elm_size = _TYPE_TO_N_BYTES[marker_type] + s += _N_BYTES_TO_START_STR[elm_size] + + while pos != end: + num = int.from_bytes(self.value[pos:pos + elm_size], + "big") + if elm_size == 1: + s += " {:02X}".format(num) + else: + s += " " + hex(num) + + pos += elm_size + + if pos != 0 and \ + (not next_marker or + next_marker[1] not in (_MarkerType.PHANDLE, _MarkerType.LABEL)): + + s += _N_BYTES_TO_END_STR[elm_size] + if pos != len(self.value): + s += "," + + return s + ";" + + + def __repr__(self): + return "" \ + .format(self.name, self.node.path, self.node.dt.filename) + + # + # Internal functions + # + + def _add_marker(self, marker_type: _MarkerType, data: Any = None): + # Helper for registering markers in the value that are processed after + # parsing. See _fixup_props(). 'marker_type' identifies the type of + # marker, and 'data' has any optional data associated with the marker. + + # len(self.value) gives the current offset. This function is called + # while the value is built. We use a list instead of a tuple to be able + # to fix up offsets later (they might increase if the value includes + # path references, e.g. 'foo = &bar, <3>;', which are expanded later). + self._markers.append([len(self.value), marker_type, data]) + + # For phandle references, add a dummy value with the same length as a + # phandle. This is handy for the length check in _register_phandles(). + if marker_type is _MarkerType.PHANDLE: + self.value += b"\0\0\0\0" + +class _T(enum.IntEnum): + # Token IDs used by the DT lexer. + + # These values must be contiguous and start from 1. + INCLUDE = 1 + LINE = 2 + STRING = 3 + DTS_V1 = 4 + PLUGIN = 5 + MEMRESERVE = 6 + BITS = 7 + DEL_PROP = 8 + DEL_NODE = 9 + OMIT_IF_NO_REF = 10 + LABEL = 11 + CHAR_LITERAL = 12 + REF = 13 + INCBIN = 14 + SKIP = 15 + EOF = 16 + + # These values must be larger than the above contiguous range. + NUM = 17 + PROPNODENAME = 18 + MISC = 19 + BYTE = 20 + BAD = 21 + +class _FileStackElt(NamedTuple): + # Used for maintaining the /include/ stack. + + filename: str + lineno: int + contents: str + pos: int + +_TokVal = Union[int, str] + +class _Token(NamedTuple): + id: int + val: _TokVal + +class DT: + """ + Represents a devicetree parsed from a .dts file (or from many files, if the + .dts file /include/s other files). Creating many instances of this class is + fine. The library has no global state. + + These attributes are available on DT instances: + + root: + A Node instance representing the root (/) node. + + alias2node: + A dictionary that maps maps alias strings (from /aliases) to Node + instances + + label2node: + A dictionary that maps each node label (a string) to the Node instance + for the node. + + label2prop: + A dictionary that maps each property label (a string) to a Property + instance. + + label2prop_offset: + A dictionary that maps each label (a string) within a property value + (e.g., 'x = label_1: < 1 label2: 2 >;') to a (prop, offset) tuple, where + 'prop' is a Property instance and 'offset' the byte offset (0 for label_1 + and 4 for label_2 in the example). + + phandle2node: + A dictionary that maps each phandle (a number) to a Node instance. + + memreserves: + A list of (labels, address, length) tuples for the /memreserve/s in the + .dts file, in the same order as they appear in the file. + + 'labels' is a possibly empty set with all labels preceding the memreserve + (e.g., 'label1: label2: /memreserve/ ...'). 'address' and 'length' are + numbers. + + filename: + The filename passed to the DT constructor. + """ + + # + # Public interface + # + + def __init__(self, filename: str, include_path: Iterable[str] = (), + force: bool = False): + """ + Parses a DTS file to create a DT instance. Raises OSError if 'filename' + can't be opened, and DTError for any parse errors. + + filename: + Path to the .dts file to parse. + + include_path: + An iterable (e.g. list or tuple) containing paths to search for + /include/d and /incbin/'d files. By default, files are only looked up + relative to the .dts file that contains the /include/ or /incbin/. + + force: + Try not to raise DTError even if the input tree has errors. + For experimental use; results not guaranteed. + """ + self.filename = filename + self._include_path = list(include_path) + self._force = force + + with open(filename, encoding="utf-8") as f: + self._file_contents = f.read() + + self._tok_i = self._tok_end_i = 0 + self._filestack: List[_FileStackElt] = [] + + self.alias2node: Dict[str, Node] = {} + + self._lexer_state: int = _DEFAULT + self._saved_token: Optional[_Token] = None + + self._lineno: int = 1 + + self._root: Optional[Node] = None + + self._parse_dt() + + self._register_phandles() + self._fixup_props() + self._register_aliases() + self._remove_unreferenced() + self._register_labels() + + @property + def root(self) -> Node: + """ + See the class documentation. + """ + # This is necessary because mypy can't tell that we never + # treat self._root as a non-None value until it's initialized + # properly in _parse_dt(). + return self._root # type: ignore + + def get_node(self, path: str) -> Node: + """ + Returns the Node instance for the node with path or alias 'path' (a + string). Raises DTError if the path or alias doesn't exist. + + For example, both dt.get_node("/foo/bar") and dt.get_node("bar-alias") + will return the 'bar' node below: + + /dts-v1/; + + / { + foo { + bar_label: bar { + baz { + }; + }; + }; + + aliases { + bar-alias = &bar-label; + }; + }; + + Fetching subnodes via aliases is supported: + dt.get_node("bar-alias/baz") returns the 'baz' node. + """ + if path.startswith("/"): + return _root_and_path_to_node(self.root, path, path) + + # Path does not start with '/'. First component must be an alias. + alias, _, rest = path.partition("/") + if alias not in self.alias2node: + _err("no alias '{}' found -- did you forget the leading '/' in " + "the node path?".format(alias)) + + return _root_and_path_to_node(self.alias2node[alias], rest, path) + + def has_node(self, path: str) -> bool: + """ + Returns True if the path or alias 'path' exists. See Node.get_node(). + """ + try: + self.get_node(path) + return True + except DTError: + return False + + def node_iter(self) -> Iterable[Node]: + """ + Returns a generator for iterating over all nodes in the devicetree. + + For example, this will print the name of each node that has a property + called 'foo': + + for node in dt.node_iter(): + if "foo" in node.props: + print(node.name) + """ + yield from self.root.node_iter() + + def __str__(self): + """ + Returns a DTS representation of the devicetree. Called automatically if + the DT instance is print()ed. + """ + s = "/dts-v1/;\n\n" + + if self.memreserves: + for labels, address, offset in self.memreserves: + # List the labels in a consistent order to help with testing + for label in labels: + s += label + ": " + s += "/memreserve/ {:#018x} {:#018x};\n" \ + .format(address, offset) + s += "\n" + + return s + str(self.root) + + def __repr__(self): + """ + Returns some information about the DT instance. Called automatically if + the DT instance is evaluated. + """ + return "DT(filename='{}', include_path={})" \ + .format(self.filename, self._include_path) + + # + # Parsing + # + + def _parse_dt(self): + # Top-level parsing loop + + self._parse_header() + self._parse_memreserves() + + while True: + tok = self._next_token() + + if tok.val == "/": + # '/ { ... };', the root node + if not self._root: + self._root = Node(name="/", parent=None, dt=self) + self._parse_node(self.root) + + elif tok.id in (_T.LABEL, _T.REF): + # '&foo { ... };' or 'label: &foo { ... };'. The C tools only + # support a single label here too. + + if tok.id == _T.LABEL: + label = tok.val + tok = self._next_token() + if tok.id != _T.REF: + self._parse_error("expected label reference (&foo)") + else: + label = None + + try: + node = self._ref2node(tok.val) + except DTError as e: + self._parse_error(e) + node = self._parse_node(node) + + if label: + _append_no_dup(node.labels, label) + + elif tok.id == _T.DEL_NODE: + self._next_ref2node()._del() + self._expect_token(";") + + elif tok.id == _T.OMIT_IF_NO_REF: + self._next_ref2node()._omit_if_no_ref = True + self._expect_token(";") + + elif tok.id == _T.EOF: + if not self._root: + self._parse_error("no root node defined") + return + + else: + self._parse_error("expected '/' or label reference (&foo)") + + def _parse_header(self): + # Parses /dts-v1/ (expected) and /plugin/ (unsupported) at the start of + # files. There may be multiple /dts-v1/ at the start of a file. + + has_dts_v1 = False + + while self._peek_token().id == _T.DTS_V1: + has_dts_v1 = True + self._next_token() + self._expect_token(";") + # /plugin/ always comes after /dts-v1/ + if self._peek_token().id == _T.PLUGIN: + self._parse_error("/plugin/ is not supported") + + if not has_dts_v1: + self._parse_error("expected '/dts-v1/;' at start of file") + + def _parse_memreserves(self): + # Parses /memreserve/, which appears after /dts-v1/ + + self.memreserves = [] + while True: + # Labels before /memreserve/ + labels = [] + while self._peek_token().id == _T.LABEL: + _append_no_dup(labels, self._next_token().val) + + if self._peek_token().id == _T.MEMRESERVE: + self._next_token() + self.memreserves.append( + (labels, self._eval_prim(), self._eval_prim())) + self._expect_token(";") + elif labels: + self._parse_error("expected /memreserve/ after labels at " + "beginning of file") + else: + return + + def _parse_node(self, node): + # Parses the '{ ... };' part of 'node-name { ... };'. Returns the new + # Node. + + self._expect_token("{") + while True: + labels, omit_if_no_ref = self._parse_propnode_labels() + tok = self._next_token() + + if tok.id == _T.PROPNODENAME: + if self._peek_token().val == "{": + # ' { ...', expect node + + if tok.val.count("@") > 1: + self._parse_error("multiple '@' in node name") + + # Fetch the existing node if it already exists. This + # happens when overriding nodes. + child = node.nodes.get(tok.val) or \ + Node(name=tok.val, parent=node, dt=self) + + for label in labels: + _append_no_dup(child.labels, label) + + if omit_if_no_ref: + child._omit_if_no_ref = True + + node.nodes[child.name] = child + self._parse_node(child) + + else: + # Not ' { ...', expect property assignment + + if omit_if_no_ref: + self._parse_error( + "/omit-if-no-ref/ can only be used on nodes") + + prop = node._get_prop(tok.val) + + if self._check_token("="): + self._parse_assignment(prop) + elif not self._check_token(";"): + # ';' is for an empty property, like 'foo;' + self._parse_error("expected '{', '=', or ';'") + + for label in labels: + _append_no_dup(prop.labels, label) + + elif tok.id == _T.DEL_NODE: + tok2 = self._next_token() + if tok2.id != _T.PROPNODENAME: + self._parse_error("expected node name") + if tok2.val in node.nodes: + node.nodes[tok2.val]._del() + self._expect_token(";") + + elif tok.id == _T.DEL_PROP: + tok2 = self._next_token() + if tok2.id != _T.PROPNODENAME: + self._parse_error("expected property name") + node.props.pop(tok2.val, None) + self._expect_token(";") + + elif tok.val == "}": + self._expect_token(";") + return node + + else: + self._parse_error("expected node name, property name, or '}'") + + def _parse_propnode_labels(self): + # _parse_node() helpers for parsing labels and /omit-if-no-ref/s before + # nodes and properties. Returns a (