diff --git a/dts/bindings/test/vnd,adc.yaml b/dts/bindings/test/vnd,adc.yaml new file mode 100644 index 00000000000..f7b274ea3b9 --- /dev/null +++ b/dts/bindings/test/vnd,adc.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2020, Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test ADC + +compatible: "vnd,adc" + +include: adc-controller.yaml + +properties: + "#io-channel-cells": + const: 1 + +io-channel-cells: + - input diff --git a/dts/bindings/test/vnd,array-holder.yaml b/dts/bindings/test/vnd,array-holder.yaml new file mode 100644 index 00000000000..66637d30794 --- /dev/null +++ b/dts/bindings/test/vnd,array-holder.yaml @@ -0,0 +1,17 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test array container + +compatible: "vnd,array-holder" + +include: [base.yaml] + +properties: + a: {type: "array"} + b: {type: "uint8-array"} + c: {type: "string-array"} + +gpio-cells: + - pin + - flags diff --git a/dts/bindings/test/vnd,clock.yaml b/dts/bindings/test/vnd,clock.yaml new file mode 100644 index 00000000000..325c0a2b08c --- /dev/null +++ b/dts/bindings/test/vnd,clock.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2020 Linaro Ltd. +# SPDX-License-Identifier: Apache-2.0 + +description: Test Clock Controller + +compatible: "vnd,clock" + +include: [clock-controller.yaml, base.yaml] + +properties: + "#clock-cells": + const: 2 + +clock-cells: + - bus + - bits diff --git a/dts/bindings/test/vnd,disabled-compat.yaml b/dts/bindings/test/vnd,disabled-compat.yaml new file mode 100644 index 00000000000..5d074e0fa81 --- /dev/null +++ b/dts/bindings/test/vnd,disabled-compat.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test disabled compatible + +compatible: "vnd,disabled-compat" + +include: base.yaml diff --git a/dts/bindings/test/vnd,enum-holder.yaml b/dts/bindings/test/vnd,enum-holder.yaml new file mode 100644 index 00000000000..e4ff7a2cd02 --- /dev/null +++ b/dts/bindings/test/vnd,enum-holder.yaml @@ -0,0 +1,17 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test enum property container + +compatible: "vnd,enum-holder" + +include: [base.yaml] + +properties: + val: + type: string + required: true + enum: + - "zero" + - "one" + - "two" diff --git a/dts/bindings/test/vnd,gpio-one-cell.yaml b/dts/bindings/test/vnd,gpio-one-cell.yaml new file mode 100644 index 00000000000..e8b5fa1ff34 --- /dev/null +++ b/dts/bindings/test/vnd,gpio-one-cell.yaml @@ -0,0 +1,21 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test GPIO node with one cell + +compatible: "vnd,gpio-one-cell" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + + label: + required: true + + "#gpio-cells": + const: 1 + +gpio-cells: + - pin diff --git a/dts/bindings/test/vnd,gpio.yaml b/dts/bindings/test/vnd,gpio.yaml new file mode 100644 index 00000000000..e09f22eaf48 --- /dev/null +++ b/dts/bindings/test/vnd,gpio.yaml @@ -0,0 +1,25 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test GPIO node + +compatible: "vnd,gpio" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + + label: + required: true + + "#gpio-cells": + const: 2 + +gpio-cells: + - pin + - flags + +foo-cells: + - foocell diff --git a/dts/bindings/test/vnd,i2c-device.yaml b/dts/bindings/test/vnd,i2c-device.yaml new file mode 100644 index 00000000000..a22ec3b178e --- /dev/null +++ b/dts/bindings/test/vnd,i2c-device.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test I2C device + +compatible: "vnd,i2c-device" + +include: i2c-device.yaml diff --git a/dts/bindings/test/vnd,i2c.yaml b/dts/bindings/test/vnd,i2c.yaml new file mode 100644 index 00000000000..39f5b5b8fe3 --- /dev/null +++ b/dts/bindings/test/vnd,i2c.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test I2C node + +compatible: "vnd,i2c" + +include: [i2c-controller.yaml, base.yaml] diff --git a/dts/bindings/test/vnd,intc.yaml b/dts/bindings/test/vnd,intc.yaml new file mode 100644 index 00000000000..ace8f796cba --- /dev/null +++ b/dts/bindings/test/vnd,intc.yaml @@ -0,0 +1,19 @@ +# Copyright (c) 2020 Linaro Ltd. +# SPDX-License-Identifier: Apache-2.0 + +description: Test Interrupt Controller + +compatible: "vnd,intc" + +include: [interrupt-controller.yaml, base.yaml] + +properties: + reg: + required: true + + "#interrupt-cells": + const: 2 + +interrupt-cells: + - irq + - priority diff --git a/dts/bindings/test/vnd,interrupt-holder.yaml b/dts/bindings/test/vnd,interrupt-holder.yaml new file mode 100644 index 00000000000..38d611ad4d3 --- /dev/null +++ b/dts/bindings/test/vnd,interrupt-holder.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2020 Linaro Ltd. +# SPDX-License-Identifier: Apache-2.0 + +description: Test Interrupt Controller + +compatible: "vnd,interrupt-holder" + +include: [base.yaml] + +properties: + interrupts: + required: true + + interrupt-names: + required: true diff --git a/dts/bindings/test/vnd,phandle-holder.yaml b/dts/bindings/test/vnd,phandle-holder.yaml new file mode 100644 index 00000000000..b45719e1f95 --- /dev/null +++ b/dts/bindings/test/vnd,phandle-holder.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test phandle property container + +compatible: "vnd,phandle-holder" + +include: [base.yaml] + +properties: + ph: {type: "phandle"} + phs: {type: "phandles"} + pha-gpios: {type: "phandle-array"} + gpios: {type: "phandle-array"} + foos: {type: "phandle-array"} + foo-names: {type: "string-array"} diff --git a/dts/bindings/test/vnd,reg-holder.yaml b/dts/bindings/test/vnd,reg-holder.yaml new file mode 100644 index 00000000000..c2685a49e3e --- /dev/null +++ b/dts/bindings/test/vnd,reg-holder.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2020 Linaro Ltd. +# SPDX-License-Identifier: Apache-2.0 + +description: Test register property container + +compatible: "vnd,reg-holder" + +include: [base.yaml] + +properties: + reg: + required: true + + reg-names: + required: true + + misc-prop: + type: int diff --git a/dts/bindings/test/vnd,spi-device-2.yaml b/dts/bindings/test/vnd,spi-device-2.yaml new file mode 100644 index 00000000000..79983f1641b --- /dev/null +++ b/dts/bindings/test/vnd,spi-device-2.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test SPI device 2 + +compatible: "vnd,spi-device-2" + +include: spi-device.yaml diff --git a/dts/bindings/test/vnd,spi-device.yaml b/dts/bindings/test/vnd,spi-device.yaml new file mode 100644 index 00000000000..7bf2eeccc79 --- /dev/null +++ b/dts/bindings/test/vnd,spi-device.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test SPI device + +compatible: "vnd,spi-device" + +include: spi-device.yaml diff --git a/dts/bindings/test/vnd,spi.yaml b/dts/bindings/test/vnd,spi.yaml new file mode 100644 index 00000000000..edd12ad2026 --- /dev/null +++ b/dts/bindings/test/vnd,spi.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test SPI node + +compatible: "vnd,spi" + +include: [spi-controller.yaml, base.yaml] diff --git a/dts/bindings/test/vnd,temperature-sensor.yaml b/dts/bindings/test/vnd,temperature-sensor.yaml new file mode 100644 index 00000000000..8370a21754a --- /dev/null +++ b/dts/bindings/test/vnd,temperature-sensor.yaml @@ -0,0 +1,25 @@ +# Copyright (c) 2018, Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test ADC-based temperature sensor + +compatible: "vnd,adc-temp-sensor" + +include: base.yaml + +properties: + label: + required: true + + io-channels: + type: phandle-array + required: true + description: ADC conversion channels + + io-channel-names: + type: string-array + required: true + description: conversion channel names + + clocks: + required: true diff --git a/include/devicetree.h b/include/devicetree.h index 040d48114d3..a13c59655a8 100644 --- a/include/devicetree.h +++ b/include/devicetree.h @@ -1,10 +1,18 @@ /* * SPDX-License-Identifier: Apache-2.0 * Copyright (c) 2020 Nordic Semiconductor + * Copyright (c) 2020, Linaro Ltd. * * Not a generated file. Feel free to modify. */ +/** + * @file + * @brief Devicetree main header + * + * API for accessing the current application's devicetree macros. + */ + #ifndef DEVICETREE_H #define DEVICETREE_H @@ -12,4 +20,1366 @@ #include #include +#include + +/** + * @brief devicetree.h API + */ + +/* + * 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 + * _EXISTS: property is defined + * _IDX_: logical index into property + * _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 + */ + +/** + * @defgroup devicetree-generic-id Node identifiers + * @{ + */ + +/** + * @brief Node identifier for the root node in the devicetree + */ +#define DT_ROOT DT_N + +/** + * @brief Get a node identifier for a devicetree path + * + * Example devicetree fragment: + * + * / { + * soc { + * my-serial: serial@40002000 { + * status = "okay"; + * current-speed = <115200>; + * ... + * }; + * }; + * }; + * + * Example usage with @ref DT_PROP() to get current-speed: + * + * DT_PROP(DT_PATH(soc, serial_40002000), current_speed) // 115200 + * + * 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. + * + * That is: + * + * - a first argument corresponds to a child node of the root ("soc" above) + * - a second argument corresponds to a child of the first argument + * ("serial_40002000" above, from the node name "serial@40002000" + * after changing "@" to "_") + * - and so on for deeper nodes until the desired path is given + * + * @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 + * + * Example devicetree fragment: + * + * my-serial: serial@40002000 { + * label = "UART_0"; + * status = "okay"; + * current-speed = <115200>; + * ... + * }; + * + * The only node label in this example is "my-serial". + * The string "UART_0" is *not* a node label; it's the value of a + * property named "label". + * + * Example usage to get current-speed: + * + * DT_PROP(DT_NODELABEL(my_serial), current_speed) // 115200 + * + * Convert non-alphanumeric characters in the label to underscores as + * shown, and lowercase all letters. + * + * Another example devicetree fragment: + * + * cpu@0 { + * L2_0: l2-cache { + * cache-level = <2>; + * ... + * }; + * }; + * + * Example usage to get cache-level: + * + * DT_PROP(DT_NODELABEL(l2_0), cache_level) // 2 + * + * Notice how "L2_0" in the devicetree is lowercased to "l2_0" + * for this macro's 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 for an alias + * + * Example devicetree fragment: + * + * aliases { + * my-serial = &serial0; + * }; + * + * serial0: serial@40002000 { + * status = "okay"; + * current-speed = <115200>; + * ... + * }; + * + * Example usage to get current-speed: + * + * DT_PROP(DT_ALIAS(my_serial), current_speed) // 115200 + * + * Convert non-alphanumeric characters in the alias to underscores as + * shown, and lowercase all letters. + * + * @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 + * + * Instance numbers are just indexes among enabled nodes with the same + * compatible. This complicates their use outside of device drivers. + * The **only guarantees** are: + * + * - instance numbers start at 0, + * - are contiguous, and + * - exactly one is assigned for each enabled node with a matching + * compatible + * + * 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. There **is no 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: + * + * serial@40002000 { + * compatible = "vnd,soc-serial"; + * status = "okay"; + * current-speed = <115200>; + * ... + * }; + * + * Example usage to get current-speed, **assuming that** this node is + * instance number zero of the compatible "vnd,soc-serial": + * + * DT_PROP(DT_INST(0, vnd_soc_serial), current_speed) // 115200 + * + * @param inst instance number + * @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)) + +/** + * @} + */ + +/** + * @defgroup devicetree-generic-prop Property accessors + * @{ + */ + +/** + * @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 other properties, behavior is undefined. + * + * For examples, see @ref DT_PATH(), @ref DT_ALIAS(), @ref DT_NODELABEL(), + * and @ref DT_INST(). + * + * @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 not + * be a size in bytes. + * + * For properties whose binding has type array, string-array, or + * uint8-array, this expands to the number of elements in the array. + * + * For properties of type phandles or phandle-array, it expands to the + * number of phandles or phandle+specifiers respectively. + * + * 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 above 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 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, pha, idx, cell) 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 above properties. + * + * @param node_id node identifier + * @param prop a lowercase-and-underscores property with a logical length + * @param idx index to check + * @return 1 if "idx" is a valid index into the given property, + * 0 otherwise. + */ +#define DT_PROP_HAS_IDX(node_id, prop, idx) \ + ((idx) < DT_PROP_LEN(node_id, prop)) + +/** + * @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 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) + +/* + * phandle properties + * + * These are special-cased to manage the impedance mismatch between + * phandles, which are just u32_t node properties that only make sense + * within the tree itself, and C values. + */ + +/** + * @brief Get a property value from a phandle's node + * + * This is a shorthand for DT_PROP_BY_PHANDLE_IDX(node_id, ph, 0, prop). + * It helps readability when "ph" has type "phandle". + * + * @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 value of "prop" as described in the DT_PROP() documentation + */ +#define DT_PROP_BY_PHANDLE(node_id, ph, prop) \ + DT_PROP_BY_PHANDLE_IDX(node_id, ph, 0, prop) + +/** + * @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 value of "prop" as described in the DT_PROP() documentation + */ +#define DT_PROP_BY_PHANDLE_IDX(node_id, phs, idx, prop) \ + DT_PROP(DT_PHANDLE_BY_IDX(node_id, phs, idx), prop) + +/** + * @brief Get a phandle-array specifier value at an index + * + * It might help to read the argument order as being similar to + * "node->phandle[index].cell". That is, the cell value is in the + * "pha" property of "node_id". + * + * Example devicetree fragment: + * + * gpio0: gpio@12340000 { + * #gpio-cells = < 2 >; + * }; + * + * led: led_0 { + * gpios = < &gpio0 17 0x1 >; + * }; + * + * Bindings fragment for the gpio0 node: + * + * gpio-cells: + * - pin + * - flags + * + * Example usage: + * + * #define LED DT_NODELABEL(led) + * + * DT_PHA_BY_IDX(LED, gpios, pin, 0) // 17 + * DT_PHA_BY_IDX(LED, gpios, flags, 0) // 0x1 + * + * @param node_id node identifier + * @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_PHA_BY_IDX(node_id, pha, idx, cell) \ + DT_PROP(node_id, pha##_IDX_##idx##_VAL_##cell) + +/** + * @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 binding's cell name for the specifier at "pha" index 0 + * @return the cell value + */ +#define DT_PHA(node_id, pha, cell) DT_PHA_BY_IDX(node_id, pha, 0, cell) + +/** + * @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 specifier 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 with named + * components. + * + * 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 binding's cell name for the named specifier + * @return the cell value + */ +#define DT_PHA_BY_NAME(node_id, pha, name, cell) \ + DT_PROP(node_id, pha##_NAME_##name##_VAL_##cell) + +/** + * @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 components. 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"; + * }; + * + * 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" + * + * @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 node identifier for the phandle at the element named "name" + */ +#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 { ... }; + * + * 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 a node identifier for the phandle at index "idx" in "prop" + */ +#define DT_PHANDLE_BY_IDX(node_id, prop, idx) \ + DT_PROP(node_id, prop##_IDX_##idx##_PH) + +/** + * @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) + +/* + * reg property + */ + +/** + * @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) ((idx) < DT_NUM_REGS(node_id)) + +/** + * @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) + +/* + * interrupts property + */ + +/** + * @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) ((idx) < DT_NUM_IRQS(node_id)) + +/** + * @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 + * @{ + */ + +/** + * @brief Get a node identifier for a /chosen node property + * This is only valid to call if DT_HAS_CHOSEN_NODE(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) + +/** + * @} + */ + +/** + * @defgroup devicetree-generic-exist Existence checks + * @{ + */ + +/** + * @brief Does a node identifier refer to a usable node? + * + * Example uses: + * + * DT_HAS_NODE(DT_PATH(soc, i2c@12340000)) + * DT_HAS_NODE(DT_ALIAS(an_alias_name)) + * + * Tests whether a node identifier refers to a node which: + * + * - exists in the devicetree, and + * - is enabled (has status property "okay"), and + * - has a matching binding + * + * @param node_id a node identifier + * @return 1 if the node identifier refers to a usable node, + * 0 otherwise. + */ +#define DT_HAS_NODE(node_id) IS_ENABLED(DT_CAT(node_id, _EXISTS)) + +/** + * @brief Does the devicetree have any usable nodes with a compatible? + * + * Test for whether the devicetree has any usable nodes (as determined by + * @ref DT_HAS_NODE()) with a given compatible, i.e. if there is at least one + * "x" for which "DT_HAS_NODE(DT_INST(x, compat))" is 1. + * + * @param compat lowercase-and-underscores version of a compatible + * @return 0 if no nodes of the compatible are available for use, + * 1 if at least one is enabled and has a matching binding + */ +#define DT_HAS_COMPAT(compat) DT_HAS_NODE(DT_INST(0, compat)) + +/** + * @brief Get the number of enabled instances for a given compatible + * @param compat lowercase-and-underscores version of a compatible + * @return Number of enabled instances + */ +#define DT_NUM_INST(compat) UTIL_CAT(DT_N_INST, DT_DASH(compat, NUM)) + +/** + * @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) + +/** + * @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. + * + * @param node_id node identifier + * @param compat lowercase-and-underscorse compatible value + * @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 property? + * + * Tests whether a devicetree node has a property defined. This + * tests whether the property is part of the node at all, not whether + * a boolean property is true or not. + * + * @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 cell argument to + * DT_PHA_BY_IDX(node_id, pha, idx, cell) is valid. + * If it returns 0, it is an error. + * @param node_id node identifier + * @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_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 named cell value whose existence to check + * @return 1 if the named ceell 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) + +/** + * @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)) + +/** + * @} + */ + +/** + * @defgroup devicetree-generic-bus Bus helpers + * @{ + */ + +/** + * @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 Test if a node's bus type is 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 a binding's bus type as a C token, lowercased and 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 + * @{ + */ + +/** + * @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 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 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 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 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 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 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 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 + * Equivalent to DT_INST_REG_ADDR_BY_IDX(inst, 0). + * @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 + * Equivalent to DT_INST_REG_SIZE_BY_IDX(inst, 0). + * @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 + * (see @ref DT_IRQ_BY_IDX) + * @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 + * (see @ref DT_IRQ_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_COMAPT interrupt specifier's value + * Equivalent to DT_INST_IRQ_BY_IDX(inst, 0, cell). + * @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 + * Equivalent to DT_INST_IRQ(inst, irq). + * @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's label property + * Equivalent to DT_BUS_LABEL(DT_DRV_INST(inst)) + * @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 + * This is equivalent to DT_ON_BUS(DT_DRV_INST(inst), bus). + * @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 node with compatible DT_DRV_COMPAT is on a bus + * + * This is the same as logically ORing together DT_ON_BUS(node, bus) + * for every enabled node which matches compatible DT_DRV_COMPAT. + * + * It can be useful, for instance, when writing device drivers for + * hardware that supports multiple possible bus connections to the + * SoC. + * + * @param bus a binding's bus type as a C token, lowercased and without quotes + */ +#define DT_ANY_INST_ON_BUS(bus) \ + (UTIL_LISTIFY(DT_NUM_INST(DT_DRV_COMPAT), DT_INST_ON_BUS_OR, bus) 0) + +/** + * @brief Does a DT_DRV_COMPAT instance have a property? + * Equivalent to DT_NODE_HAS_PROP(DT_DRV_INST(inst), prop) + * @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 is index valid for interrupt property on a DT_DRV_COMPAT instance? + * Equivalent to DT_IRQ_HAS_IDX(DT_DRV_INST(inst), idx). + * @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? + * Equivalent to DT_IRQ_HAS_CELL_AT_IDX(DT_DRV_INST(inst), idx, cell). + * @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? + * Equivalent to DT_INST_IRQ_HAS_IDX(DT_DRV_INST(inst), 0, cell). + * @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? + * Equivalent to DT_INST_IRQ_HAS_NAME(DT_DRV_INST(inst), name). + * @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) + +/** + * @} + */ + +#include +#include +#include +#include + +/** @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, sometimes used to force expansion */ +#define DT_CAT(node_id, prop_suffix) node_id##prop_suffix +/** @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 DT_ANY_INST_ON_BUS helper */ +#define DT_INST_ON_BUS_OR(inst, bus) DT_ON_BUS(DT_DRV_INST(inst), bus) || + #endif /* DEVICETREE_H */ diff --git a/include/devicetree/adc.h b/include/devicetree/adc.h new file mode 100644 index 00000000000..6ed4d8764a3 --- /dev/null +++ b/include/devicetree/adc.h @@ -0,0 +1,198 @@ +/** + * @file + * @brief ADC Devicetree macro public API header file. + */ + +/* + * Copyright (c) 2020, Linaro Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DEVICETREE_ADC_H_ +#define ZEPHYR_INCLUDE_DEVICETREE_ADC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup devicetree-adc Devicetree ADC API + * @{ + */ + +/** + * @brief Get IO channels controller "name" (label property) at an index + * + * It's an error if the IO channels controller referenced by the phandle + * in property "io_channels" at index "idx" has no label property. + * + * Example devicetree fragment: + * + * adc1: adc@... { + * label = "ADC_1"; + * }; + * + * adc2: adc@... { + * label = "ADC_2"; + * }; + * + * n: node { + * io-channels = <&adc1 10>, <&adc2 20>; + * }; + * + * Example usage: + * + * DT_IO_CHANNELS_LABEL_BY_IDX(DT_NODELABEL(n), 1) // "ADC_2" + * + * @param node_id node identifier + * @param idx logical index into the property + * @return the label property for the referenced node at index idx + * @see DT_PHANDLE_BY_IDX() + */ +#define DT_IO_CHANNELS_LABEL_BY_IDX(node_id, idx) \ + DT_PROP_BY_PHANDLE_IDX(node_id, io_channels, idx, label) + +/** + * @brief Get IO channels controller "name" (label property) by name + * + * It's an error if the IO channels controller referenced by the phandle + * in property "io_channels" at index "idx" has no label property. + * + * 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"; + * }; + * + * Example usage: + * + * DT_IO_CHANNELS_LABEL_BY_NAME(DT_NODELABEL(n), bandgap) // "ADC_2" + * + * @param node_id node identifier + * @param name lowercase-and-underscores "io_channel" name + * @return the label property for the referenced node by name + * @see DT_PHANDLE_BY_NAME() + */ +#define DT_IO_CHANNELS_LABEL_BY_NAME(node_id, name) \ + DT_PROP(DT_PHANDLE_BY_NAME(node_id, io_channels, name), label) + +/** + * @brief Equivalent to DT_IO_CHANNELS_LABEL_BY_IDX(node_id, 0) + * @param node_id node identifier + * @return the label property for the named specifier at index 0 + * @see DT_IO_CHANNELS_LABEL_BY_IDX() + */ +#define DT_IO_CHANNELS_LABEL(node_id) DT_IO_CHANNELS_LABEL_BY_IDX(node_id, 0) + +/** + * @brief Get IO channel's controller "name" at an index + * (see @ref DT_IO_CHANNELS_LABEL_BY_IDX) + * @param inst instance number + * @param idx logical index into the property + * @return the label property for the named specifier at index idx + */ +#define DT_INST_IO_CHANNELS_LABEL_BY_IDX(inst, idx) \ + DT_IO_CHANNELS_LABEL_BY_IDX(DT_DRV_INST(inst), idx) + +/** + * @brief Get IO channel's controller "name" by name + * (see @ref DT_IO_CHANNELS_LABEL_BY_NAME) + * @param inst instance number + * @param name lowercase-and-underscores "io_channel" name + * @return the label property for the named specifier by name + */ +#define DT_INST_IO_CHANNELS_LABEL_BY_NAME(inst, name) \ + DT_IO_CHANNELS_LABEL_BY_NAME(DT_DRV_INST(inst), name) + +/** + * @brief Equivalent to DT_INST_IO_CHANNELS_LABEL_BY_IDX(inst, 0) + * @param inst instance number + * @return the label property for the named specifier at index 0 + */ +#define DT_INST_IO_CHANNELS_LABEL(inst) DT_INST_IO_CHANNELS_LABEL_BY_IDX(inst, 0) + +/** + * @brief Get IO channels controller 'input' at an index + * + * This macro only works for IO channels controllers that specify a 'input' + * field in the phandle-array specifier. Refer to the specific IO channels + * controller binding if needed. + * + * @param node_id node identifier + * @param idx logical index into the property + * @return the input value for the named specifier at index idx + * @see DT_PHA_BY_IDX() + * @see DT_PHA() + */ +#define DT_IO_CHANNELS_INPUT_BY_IDX(node_id, idx) \ + DT_PHA_BY_IDX(node_id, io_channels, idx, input) + +/** + * @brief Get IO channels controller 'input' by name + * + * This macro only works for IO channels controllers that specify a 'input' + * field in the phandle-array specifier. Refer to the specific IO channels + * controller binding if needed. + * + * @param node_id node identifier + * @param name lowercase-and-underscores "io_channel" name + * @return the input value for the named specifier by name + * @see DT_PHA_BY_NAME() + */ +#define DT_IO_CHANNELS_INPUT_BY_NAME(node_id, name) \ + DT_PHA_BY_NAME(node_id, io_channels, name, input) +/** + * @brief Equivalent to DT_IO_CHANNELS_INPUT_BY_IDX(node_id, 0) + * @param node_id node identifier + * @return the label property for the named specifier at index 0 + * @see DT_IO_CHANNELS_INPUT_BY_IDX() + */ +#define DT_IO_CHANNELS_INPUT(node_id) DT_IO_CHANNELS_INPUT_BY_IDX(node_id, 0) + +/** + * @brief Get IO channel's controller "input" at an index + * (see @ref DT_IO_CHANNELS_INPUT_BY_IDX) + * @param inst instance number + * @param idx logical index into the property + * @return the input value for the named specifier at index idx + */ +#define DT_INST_IO_CHANNELS_INPUT_BY_IDX(inst, idx) \ + DT_IO_CHANNELS_INPUT_BY_IDX(DT_DRV_INST(inst), idx) + +/** + * @brief Get IO channel's controller "input" by name + * (see @ref DT_IO_CHANNELS_INPUT_BY_NAME) + * @param inst instance number + * @param name lowercase-and-underscores "io_channel" name + * @return the input value for the named specifier at index idx + */ +#define DT_INST_IO_CHANNELS_INPUT_BY_NAME(inst, name) \ + DT_IO_CHANNELS_INPUT_BY_NAME(DT_DRV_INST(inst), name) + +/** + * @brief Equivalent to DT_INST_IO_CHANNELS_INPUT(inst, 0) + * @param inst instance number + * @return the input property for the named specifier at index 0 + */ +#define DT_INST_IO_CHANNELS_INPUT(inst) DT_INST_IO_CHANNELS_INPUT_BY_IDX(inst, 0) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + + +#endif /* ZEPHYR_INCLUDE_DEVICETREE_ADC_H_ */ diff --git a/include/devicetree/clocks.h b/include/devicetree/clocks.h new file mode 100644 index 00000000000..dedd09d6465 --- /dev/null +++ b/include/devicetree/clocks.h @@ -0,0 +1,153 @@ +/** + * @file + * @brief Clocks Devicetree macro public API header file. + */ + +/* + * Copyright (c) 2020, Linaro Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DEVICETREE_CLOCKS_H_ +#define ZEPHYR_INCLUDE_DEVICETREE_CLOCKS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup devicetree-clocks Devicetree Clocks API + * @{ + */ + +/** + * @brief Get clock controller "name" (label property) at an index + * + * It's an error if the clock controller referenced by the phandle + * in property "clocks" at index "idx" has no label property. + * + * Example devicetree fragment: + * + * clk1: clkctrl@... { + * label = "CLK_1"; + * }; + * + * clk2: clkctrl@... { + * label = "CLK_2"; + * }; + * + * n: node { + * clocks = <&clk1 10 20>, <&clk2 30 40>; + * }; + * + * Example usage: + * + * DT_CLOCKS_LABEL_BY_IDX(DT_NODELABEL(n), 1) // "CLK_2" + * + * @param node_id node identifier + * @param idx logical index into the property + * @return the label property for the referenced node at index idx + * @see DT_PROP_BY_PHANDLE_IDX() + */ +#define DT_CLOCKS_LABEL_BY_IDX(node_id, idx) \ + DT_PROP_BY_PHANDLE_IDX(node_id, clocks, idx, label) + +/** + * @brief Equivalent to DT_CLOCKS_LABEL_BY_IDX(node_id, 0) + * @param node_id node identifier + * @return the label property for the named specifier at index 0 + * @see DT_CLOCKS_LABEL_BY_IDX() + */ +#define DT_CLOCKS_LABEL(node_id) DT_CLOCKS_LABEL_BY_IDX(node_id, 0) + +/** + * @brief Get Clock controller "cell" value at an index + * + * Example devicetree fragment: + * + * clk1: clkctrl@... { + * label = "CLK_1"; + * #clock-cells = < 2 >; + * }; + * + * n: node { + * clocks = < &clk1 10 20 >, < &clk1 30 40 >; + * }; + * + * Bindings fragment for the gpio0 node: + * + * clock-cells: + * - bus + * - bits + * + * Example usage: + * + * DT_CLOCKS_CELL_BY_IDX(DT_NODELABEL(n), bus, 0) // 10 + * DT_CLOCKS_CELL_BY_IDX(DT_NODELABEL(n), bits, 1) // 40 + * + * @param node_id node identifier + * @param cell binding's cell name within the specifier at index "idx" + * @param idx logical index into the property + * @return the value of the cell inside the specifier at index "idx" + * @see DT_PHA_PHANDLE_IDX() + */ +#define DT_CLOCKS_CELL_BY_IDX(node_id, cell, idx) \ + DT_PHA_BY_IDX(node_id, clocks, idx, cell) + +/** + * @brief Equivalent to DT_CLOCKS_CELL_BY_IDX(node_id, cell, 0) + * @param node_id node identifier + * @param cell binding's cell name within the specifier at index 0 + * @return the value of the cell inside the specifier at index 0 + * @see DT_CLOCKS_CELL_BY_IDX() + */ +#define DT_CLOCKS_CELL(node_id, cell) DT_CLOCKS_CELL_BY_IDX(node_id, cell, 0) + +/** + * @brief Get a DT_DRV_COMPAT clock controller "name" at an index + * (see @ref DT_CLOCKS_LABEL_BY_IDX) + * @param inst instance number + * @param idx logical index into the property + * @return the label property for the named specifier at index idx + */ +#define DT_INST_CLOCKS_LABEL_BY_IDX(inst, idx) \ + DT_CLOCKS_LABEL_BY_IDX(DT_DRV_INST(inst), idx) + +/** + * @brief Get a DT_DRV_COMPAT clock controller "name" + * (see @ref DT_CLOCKS_LABEL_BY_IDX) + * @param inst instance number + * @return the label property for the named specifier at index 0 + */ +#define DT_INST_CLOCKS_LABEL(inst) DT_INST_CLOCKS_LABEL_BY_IDX(inst, 0) + +/** + * @brief Get a DT_DRV_COMPAT clock controller "cell" value at an index + * @param inst instance number + * @param cell binding's cell name within the specifier at index "idx" + * @param idx logical index into the property + * @return the value of the cell inside the specifier at index "idx" + */ +#define DT_INST_CLOCKS_CELL_BY_IDX(inst, cell, idx) \ + DT_CLOCKS_CELL_BY_IDX(DT_DRV_INST(inst), cell, idx) + +/** + * @brief Get a DT_DRV_COMPAT clock controller "cell" value at an index 0 + * @param inst instance number + * @param cell binding's cell name within the specifier at index 0 + * @return the value of the cell inside the specifier at index 0 + */ +#define DT_INST_CLOCKS_CELL(inst, cell) \ + DT_INST_CLOCKS_CELL_BY_IDX(inst, cell, 0) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + + +#endif /* ZEPHYR_INCLUDE_DEVICETREE_CLOCKS_H_ */ diff --git a/include/devicetree/gpio.h b/include/devicetree/gpio.h new file mode 100644 index 00000000000..485c7407455 --- /dev/null +++ b/include/devicetree/gpio.h @@ -0,0 +1,202 @@ +/** + * @file + * @brief GPIO Devicetree macro public API header file. + */ + +/* + * Copyright (c) 2020, Linaro Ltd. + * Copyright (c) 2020 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DEVICETREE_GPIO_H_ +#define ZEPHYR_INCLUDE_DEVICETREE_GPIO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup devicetree-gpio Devicetree GPIO API + * @{ + */ + +/** + * @brief Get gpio controller "name" (label property) at an index + * + * It's an error if the GPIO controller referenced by the phandle + * in property "gpio_pha" at index "idx" has no label property. + * + * Example devicetree fragment: + * + * gpio1: gpio@... { + * label = "GPIO_1"; + * }; + * + * gpio2: gpio@... { + * label = "GPIO_2"; + * }; + * + * n: node { + * gpios = <&gpio1 10 20>, <&gpio2 30 40>; + * }; + * + * Example usage: + * + * DT_GPIO_LABEL_BY_IDX(DT_NODELABEL(n), gpios, 1) // "GPIO_2" + * + * @param node_id node identifier + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @param idx logical index into the property + * @return the label property for the referenced node at index idx + * @see DT_PHANDLE_BY_IDX() + */ +#define DT_GPIO_LABEL_BY_IDX(node_id, gpio_pha, idx) \ + DT_PROP_BY_PHANDLE_IDX(node_id, gpio_pha, idx, label) + +/** + * @brief Equivalent to DT_GPIO_LABEL_BY_IDX(node_id, gpio_pha, 0) + * @param node_id node identifier + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @return the label property for the named specifier at index 0 + * @see DT_GPIO_LABEL_BY_IDX() + */ +#define DT_GPIO_LABEL(node_id, gpio_pha) \ + DT_GPIO_LABEL_BY_IDX(node_id, gpio_pha, 0) + +/** + * @brief Get gpio controller 'pin' at an index + * + * This macro only works for GPIO controllers that specify a 'pin' + * field in the phandle-array specifier. Refer to the specific GPIO + * controller binding if needed. + * + * @param node_id node identifier + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @param idx logical index into the property + * @return the pin value for the named specifier at index idx + * @see DT_PHA_BY_IDX() + * @see DT_PHA() + */ +#define DT_GPIO_PIN_BY_IDX(node_id, gpio_pha, idx) \ + DT_PHA_BY_IDX(node_id, gpio_pha, idx, pin) + +/** + * @brief Equivalent to DT_GPIO_PIN_BY_IDX(node_id, gpio_pha, 0) + * @param node_id node identifier + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @return the pin value for the named specifier at index idx + * @see DT_GPIO_PIN_BY_IDX() + */ +#define DT_GPIO_PIN(node_id, gpio_pha) \ + DT_GPIO_PIN_BY_IDX(node_id, gpio_pha, 0) + +/** + * @brief Get gpio controller 'flags' at an index + * + * This macro only works for GPIO controllers that specify a 'flags' + * field in the phandle-array specifier. Refer to the specific GPIO + * controller binding if needed. + * + * @param node_id node identifier + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @param idx logical index into the property + * @return the flags value for the named specifier at index idx + * @see DT_PHA_BY_IDX() + * @see DT_PHA() + */ +#define DT_GPIO_FLAGS_BY_IDX(node_id, gpio_pha, idx) \ + DT_PHA_BY_IDX(node_id, gpio_pha, idx, flags) + +/** + * @brief Equivalent to DT_GPIO_FLAGS_BY_IDX(node_id, gpio_pha, 0) + * @param node_id node identifier + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @return the flags value for the named specifier at index idx + * @see DT_GPIO_FLAGS_BY_IDX() + */ +#define DT_GPIO_FLAGS(node_id, gpio_pha) \ + DT_GPIO_FLAGS_BY_IDX(node_id, gpio_pha, 0) + +/** + * @brief Get gpio controller "name" at an index (see @ref DT_GPIO_LABEL_BY_IDX) + * @param inst instance number + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @param idx logical index into the property + * @return the label property for the named specifier at index idx + */ +#define DT_INST_GPIO_LABEL_BY_IDX(inst, gpio_pha, idx) \ + DT_GPIO_LABEL_BY_IDX(DT_DRV_INST(inst), gpio_pha, idx) + +/** + * @brief Equivalent to DT_INST_GPIO_LABEL_BY_IDX(inst, gpio_pha, 0) + * @param inst instance number + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @return the label property for the named specifier at index 0 + */ +#define DT_INST_GPIO_LABEL(inst, gpio_pha) \ + DT_INST_GPIO_LABEL_BY_IDX(inst, gpio_pha, 0) + +/** + * @brief Get gpio controller "pin" at an index (see @ref DT_GPIO_PIN_BY_IDX) + * @param inst instance number + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @param idx logical index into the property + * @return the pin value for the named specifier at index idx + */ +#define DT_INST_GPIO_PIN_BY_IDX(inst, gpio_pha, idx) \ + DT_GPIO_PIN_BY_IDX(DT_DRV_INST(inst), gpio_pha, idx) + +/** + * @brief Equivalent to DT_INST_GPIO_PIN_BY_IDX(inst, gpio_pha, 0) + * @param inst instance number + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @return the pin value for the named specifier at index 0 + * @see DT_INST_GPIO_PIN_BY_IDX() + */ +#define DT_INST_GPIO_PIN(inst, gpio_pha) \ + DT_INST_GPIO_PIN_BY_IDX(inst, gpio_pha, 0) + +/** + * @brief Get a devicetree property value (see @ref DT_GPIO_FLAGS_BY_IDX) + * @param inst instance number + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @param idx logical index into the property + * @return a representation of the property's value + */ +#define DT_INST_GPIO_FLAGS_BY_IDX(inst, gpio_pha, idx) \ + DT_GPIO_FLAGS_BY_IDX(DT_DRV_INST(inst), gpio_pha, idx) + +/** + * @brief Equivalent to DT_INST_GPIO_FLAGS_BY_IDX(inst, gpio_pha, 0) + * @param inst instance number + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @return the flags value for the named specifier at index 0 + * @see DT_INST_GPIO_FLAGS_BY_IDX() + */ +#define DT_INST_GPIO_FLAGS(inst, gpio_pha) \ + DT_INST_GPIO_FLAGS_BY_IDX(inst, gpio_pha, 0) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + + +#endif /* ZEPHYR_INCLUDE_DEVICETREE_GPIO_H_ */ diff --git a/include/devicetree/spi.h b/include/devicetree/spi.h new file mode 100644 index 00000000000..9632f44dd32 --- /dev/null +++ b/include/devicetree/spi.h @@ -0,0 +1,121 @@ +/** + * @file + * @brief SPI Devicetree macro public API header file. + */ + +/* + * Copyright (c) 2020 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DEVICETREE_SPI_H_ +#define ZEPHYR_INCLUDE_DEVICETREE_SPI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup devicetree-spi Devicetree SPI API + * @{ + */ + +/** + * @brief Does a SPI controller have chip select GPIOs configured? + * The commonly used "cs-gpios" property is used for this test. + * @param spi node identifier for a SPI bus controller + * @return 1 if it has a cs-gpios property, 0 otherwise + */ +#define DT_SPI_HAS_CS(spi) DT_NODE_HAS_PROP(spi, cs_gpios) + +/** + * @brief The number of chip select GPIOs in a SPI controller + * @param spi node identifier for a SPI bus controller + * @return The length of its cs-gpios, or 0 if it doesn't have one + */ +#define DT_SPI_NUM_CS(spi) \ + COND_CODE_1(DT_SPI_HAS_CS(spi), \ + (DT_PROP_LEN(spi, cs_gpios)), (0)) + +/** + * @brief Does a SPI device have a chip select line in DT? + * @param spi_dev node identifier for a SPI device + * @return 1 if the SPI device's bus DT_BUS(spi_dev) has a CS + * pin at index DT_REG_ADDR(spi_dev), 0 otherwise + */ +#define DT_SPI_DEV_HAS_CS(spi_dev) DT_SPI_HAS_CS(DT_BUS(spi_dev)) + +/** + * @brief Get GPIO controller name for a SPI device's chip select + * DT_SPI_DEV_HAS_CS(spi_dev) must expand to 1. + * @brief spi_dev a SPI device node identifier + * @return label property of spi_dev's chip select GPIO controller + */ +#define DT_SPI_DEV_CS_GPIO_LABEL(spi_dev) \ + DT_GPIO_LABEL_BY_IDX(DT_BUS(spi_dev), cs_gpios, DT_REG_ADDR(spi_dev)) + +/** + * @brief Get GPIO specifier 'pin' value for a SPI device's chip select + * It's an error if the GPIO specifier for spi_dev's entry in its + * bus node's cs-gpios property has no 'pin' value. + * @brief spi_dev a SPI device node identifier + * @return pin number of spi_dev's chip select GPIO + */ +#define DT_SPI_DEV_CS_GPIO_PIN(spi_dev) \ + DT_GPIO_PIN_BY_IDX(DT_BUS(spi_dev), cs_gpios, DT_REG_ADDR(spi_dev)) + +/** + * @brief Get GPIO specifier 'flags' value for a SPI device's chip select + * It's an error if the GPIO specifier for spi_dev's entry in its + * bus node's cs-gpios property has no 'flags' value. + * @brief spi_dev a SPI device node identifier + * @return flags value of spi_dev's chip select GPIO specifier + */ +#define DT_SPI_DEV_CS_GPIO_FLAGS(spi_dev) \ + DT_GPIO_FLAGS_BY_IDX(DT_BUS(spi_dev), cs_gpios, DT_REG_ADDR(spi_dev)) + +/** + * @brief Equivalent to DT_SPI_DEV_HAS_CS(DT_DRV_INST(inst)) + * @param inst instance number + * @return 1 if the instance's bus has a CS pin at index + * DT_INST_REG_ADDR(inst), 0 otherwise + */ +#define DT_INST_SPI_DEV_HAS_CS(inst) DT_SPI_DEV_HAS_CS(DT_DRV_INST(inst)) + +/** + * @brief Get GPIO controller name for a SPI device instance + * This is equivalent to DT_SPI_DEV_CS_GPIO_LABEL(DT_DRV_INST(inst)). + * @brief inst instance number + * @return label property of the instance's chip select GPIO controller + */ +#define DT_INST_SPI_DEV_CS_GPIO_LABEL(inst) \ + DT_SPI_DEV_CS_GPIO_LABEL(DT_DRV_INST(inst)) + +/** + * @brief Get GPIO specifier "pin" value for a SPI device instance + * This is equivalent to DT_SPI_DEV_CS_GPIO_PIN(DT_DRV_INST(inst)). + * @brief inst a SPI device instance number + * @return pin number of the instance's chip select GPIO + */ +#define DT_INST_SPI_DEV_CS_GPIO_PIN(inst) \ + DT_SPI_DEV_CS_GPIO_PIN(DT_DRV_INST(inst)) + +/** + * @brief Get GPIO specifier "flags" value for a SPI device instance + * This is equivalent to DT_SPI_DEV_CS_GPIO_FLAGS(DT_DRV_INST(inst)). + * @brief inst a SPI device instance number + * @return flags value of the instance's chip select GPIO specifier + */ +#define DT_INST_SPI_DEV_CS_GPIO_FLAGS(inst) \ + DT_SPI_DEV_CS_GPIO_FLAGS(DT_DRV_INST(inst)) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DEVICETREE_SPI_H_ */ diff --git a/tests/lib/devicetree/CMakeLists.txt b/tests/lib/devicetree/CMakeLists.txt new file mode 100644 index 00000000000..33d1fefb4e2 --- /dev/null +++ b/tests/lib/devicetree/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) + +set(DTS_ROOTS ${CMAKE_CURRENT_LIST_DIR}) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(devicetree) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/lib/devicetree/README b/tests/lib/devicetree/README new file mode 100644 index 00000000000..a1a1a9a671b --- /dev/null +++ b/tests/lib/devicetree/README @@ -0,0 +1 @@ +Test cases for the devicetree.h API. diff --git a/tests/lib/devicetree/app.overlay b/tests/lib/devicetree/app.overlay new file mode 100644 index 00000000000..568ff9a7e6a --- /dev/null +++ b/tests/lib/devicetree/app.overlay @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + * + * Application overlay for testing the devicetree.h API. + * + * Names in this file should be chosen in a way that won't conflict + * with real-world devicetree nodes, to allow these tests to run on + * (and be extended to test) real hardware. + */ + +/ { + aliases { + test-alias = &test_nodelabel; + }; + + chosen { + ztest,gpio = &test_nodelabel; + }; + + test { + #address-cells = < 0x1 >; + #size-cells = < 0x1 >; + interrupt-parent = <&test_intc>; + + test_arrays: array-holder { + /* vnd,undefined-compat is for DT_NODE_HAS_COMPAT() */ + compatible = "vnd,array-holder", "vnd,undefined-compat"; + a = <1000 2000 3000>; + b = [aa bb cc dd]; + c = "bar", "baz"; + }; + + test_phandles: phandle-holder-0 { + compatible = "vnd,phandle-holder"; + ph = <&test_gpio_1>; + phs = <&test_gpio_1 &test_gpio_2 &test_i2c>; + gpios = <&test_gpio_1 10 20>, <&test_gpio_2 30 40>; + pha-gpios = <&test_gpio_1 50 60>, <&test_gpio_3 70>, <&test_gpio_2 80 90>; + foos = <&test_gpio_1 100>, <&test_gpio_2 110>; + foo-names = "A", "b-c"; + }; + + test_enum_0: enum-0 { + compatible = "vnd,enum-holder"; + val = "zero"; + }; + + test_enum_1: enum-1 { + compatible = "vnd,enum-holder"; + val = "two"; + }; + + /* + * This should be the only node with this + * compatible in the tree. + */ + disabled-node@0 { + compatible = "vnd,disabled-compat"; + reg = < 0x0 0x1000 >; + label = "DISABLED_NODE_0"; + status = "disabled"; + }; + + disabled_gpio: gpio@0 { + compatible = "vnd,gpio"; + gpio-controller; + reg = < 0x0 0x1000 >; + interrupts = <3 1>; + #gpio-cells = < 0x2 >; + label = "TEST_GPIO_0"; + status = "disabled"; + }; + + test_nodelabel: TEST_NODELABEL_ALLCAPS: test_gpio_1: gpio@deadbeef { + compatible = "vnd,gpio"; + gpio-controller; + reg = < 0xdeadbeef 0x1000 >; + #gpio-cells = < 0x2 >; + #foo-cells = < 0x1 >; + label = "TEST_GPIO_1"; + interrupts = <4 3>; + status = "okay"; + }; + + test_gpio_2: gpio@abcd1234 { + compatible = "vnd,gpio"; + gpio-controller; + reg = < 0xabcd1234 0x500 0x98765432 0xff >; + reg-names = "one", "two"; + #gpio-cells = < 0x2 >; + #foo-cells = < 0x1 >; + interrupts = <5 2>; + label = "TEST_GPIO_2"; + status = "okay"; + }; + + test_gpio_3: gpio@1234 { + compatible = "vnd,gpio-one-cell"; + gpio-controller; + reg = < 0x1234 0x500 >; + #gpio-cells = < 0x1 >; + label = "TEST_GPIO_3"; + status = "okay"; + }; + + test_i2c: i2c@11112222 { + #address-cells = < 1 >; + #size-cells = < 0 >; + compatible = "vnd,i2c"; + reg = < 0x11112222 0x1000 >; + label = "TEST_I2C_CTLR"; + status = "okay"; + clock-frequency = < 100000 >; + interrupts = <6 2 7 1>; + interrupt-names = "status", "error"; + + test-i2c-dev@10 { + compatible = "vnd,i2c-device"; + label = "TEST_I2C_DEV_10"; + reg = < 0x10 >; + }; + }; + + test_spi: spi@33334444 { + #address-cells = < 1 >; + #size-cells = < 0 >; + compatible = "vnd,spi"; + reg = < 0x33334444 0x1000 >; + interrupts = <8 3 9 0 10 1>; + label = "TEST_SPI_CTLR"; + status = "okay"; + clock-frequency = < 2000000 >; + + cs-gpios = <&test_gpio_1 0x10 0x20>, + <&test_gpio_2 0x30 0x40>; + + /* all vnd,spi-device instances should have CS */ + + test-spi-dev@0 { + compatible = "vnd,spi-device"; + label = "TEST_SPI_DEV_0"; + reg = <0>; + spi-max-frequency = < 2000000 >; + }; + + test-spi-dev@1 { + compatible = "vnd,spi-device"; + label = "TEST_SPI_DEV_1"; + reg = <1>; + spi-max-frequency = < 2000000 >; + }; + }; + + test_spi_no_cs: spi@55556666 { + #address-cells = < 1 >; + #size-cells = < 0 >; + compatible = "vnd,spi"; + reg = < 0x55556666 0x1000 >; + label = "TEST_SPI_CTLR_NO_CS"; + status = "okay"; + clock-frequency = < 2000000 >; + + /* no vnd,spi-device-2 instances should have CS */ + test_spi_dev_no_cs: test-spi-dev@0 { + compatible = "vnd,spi-device-2"; + label = "TEST_SPI_DEV_NO_CS"; + reg = <0>; + spi-max-frequency = < 2000000 >; + }; + }; + + test_i2c_1: i2c@77778888 { + #address-cells = < 1 >; + #size-cells = < 0 >; + compatible = "vnd,i2c"; + reg = < 0x77778888 0x1000 >; + label = "TEST_I2C_CTLR_1"; + status = "okay"; + clock-frequency = < 100000 >; + interrupts = <11 3 12 2>; + interrupt-names = "status", "error"; + }; + + test_adc_1: adc@10002000 { + reg = <0x10002000 0x1000>; + compatible = "vnd,adc"; + label = "TEST_ADC_1"; + status = "okay"; + #io-channel-cells = <1>; + }; + + test_adc_2: adc@10003000 { + reg = <0x10003000 0x1000>; + compatible = "vnd,adc"; + label = "TEST_ADC_2"; + status = "okay"; + #io-channel-cells = <1>; + }; + + /* there should only be one of these */ + test_temp_sensor: temperature-sensor { + compatible = "vnd,adc-temp-sensor"; + label = "TEST_TEMP"; + io-channels = <&test_adc_1 10>, <&test_adc_2 20>; + io-channel-names = "ch1", "ch2"; + clocks = <&test_clk 3 7>, <&test_fixed_clk>, <&test_clk 8 2>; + }; + + /* there should only be one of these */ + test_reg: reg-holder@9999aaaa { + compatible = "vnd,reg-holder"; + reg = < 0x9999aaaa 0x1000 0xbbbbcccc 0x3f >; + status = "okay"; + reg-names = "first", "second"; + misc-prop = <1234>; + }; + + test_intc: interrupt-controller@bbbbcccc { + compatible = "vnd,intc"; + reg = <0xbbbbcccc 0x1000>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + /* there should only be one of these */ + test_irq: interrupt-holder { + compatible = "vnd,interrupt-holder"; + status = "okay"; + interrupts = <30 3 40 5 60 7>; + interrupt-names = "err", "stat", "done"; + }; + + test_fixed_clk: test-fixed-clock { + compatible = "fixed-clock"; + clock-frequency = <25000000>; + #clock-cells = <0>; + }; + + test_clk: test-clock { + compatible = "vnd,clock"; + label = "TEST_CLOCK"; + #clock-cells = <2>; + }; + }; +}; diff --git a/tests/lib/devicetree/prj.conf b/tests/lib/devicetree/prj.conf new file mode 100644 index 00000000000..9467c292689 --- /dev/null +++ b/tests/lib/devicetree/prj.conf @@ -0,0 +1 @@ +CONFIG_ZTEST=y diff --git a/tests/lib/devicetree/src/main.c b/tests/lib/devicetree/src/main.c new file mode 100644 index 00000000000..3136d3223ba --- /dev/null +++ b/tests/lib/devicetree/src/main.c @@ -0,0 +1,1136 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define TEST_DEADBEEF DT_PATH(test, gpio_deadbeef) +#define TEST_ABCD1234 DT_PATH(test, gpio_abcd1234) +#define TEST_ALIAS DT_ALIAS(test_alias) +#define TEST_NODELABEL DT_NODELABEL(test_nodelabel) +#define TEST_INST DT_INST(0, vnd_gpio) +#define TEST_ARRAYS DT_NODELABEL(test_arrays) +#define TEST_PH DT_NODELABEL(test_phandles) +#define TEST_IRQ DT_NODELABEL(test_irq) +#define TEST_TEMP DT_NODELABEL(test_temp_sensor) + +static void test_path_props(void) +{ + zassert_true(!strcmp(DT_LABEL(TEST_DEADBEEF), "TEST_GPIO_1"), + "deadbeef label != TEST_GPIO_1"); + zassert_equal(DT_NUM_REGS(TEST_DEADBEEF), 1, + "deadbeef num reg"); + zassert_equal(DT_REG_ADDR(TEST_DEADBEEF), 0xdeadbeef, + "deadbeef reg[0] addr"); + zassert_equal(DT_REG_SIZE(TEST_DEADBEEF), 0x1000, + "deadbeef reg[0] len"); + zassert_equal(DT_PROP(TEST_DEADBEEF, gpio_controller), 1, + "deadbeef gpio-controller"); + zassert_equal(DT_PROP(TEST_DEADBEEF, ngpios), 32, "deadbeef ngpios"); + zassert_true(!strcmp(DT_PROP(TEST_DEADBEEF, status), "okay"), + "deadbeef status"); + zassert_equal(DT_PROP_LEN(TEST_DEADBEEF, compatible), 1, + "deadbeef compatible len"); + zassert_true(!strcmp(DT_PROP_BY_IDX(TEST_DEADBEEF, compatible, 0), + "vnd,gpio"), + "deadbeef compatible[0]"); + zassert_true(DT_NODE_HAS_PROP(TEST_DEADBEEF, status), + "deadbeef status"); + zassert_false(DT_NODE_HAS_PROP(TEST_DEADBEEF, foobar), + "deadbeef foobar"); + + zassert_true(!strcmp(DT_LABEL(TEST_ABCD1234), "TEST_GPIO_2"), + "abcd1234 label != TEST_GPIO_2"); + zassert_equal(DT_NUM_REGS(TEST_ABCD1234), 2, + "abcd1234 num regs"); + zassert_equal(DT_PROP(TEST_ABCD1234, gpio_controller), 1, + "abcd1234 gpio-controller"); + zassert_equal(DT_PROP(TEST_ABCD1234, ngpios), 32, "abcd1234 ngpios"); + zassert_true(!strcmp(DT_PROP(TEST_ABCD1234, status), "okay"), + "abcd1234 status"); + zassert_equal(DT_PROP_LEN(TEST_ABCD1234, compatible), 1, + "abcd1234 compatible len"); + zassert_true(!strcmp(DT_PROP_BY_IDX(TEST_ABCD1234, compatible, 0), + "vnd,gpio"), + "abcd1234 compatible[0]"); +} + +static void test_alias_props(void) +{ + zassert_equal(DT_NUM_REGS(TEST_ALIAS), 1, "num regs"); + zassert_equal(DT_REG_ADDR(TEST_ALIAS), 0xdeadbeef, "reg[0] addr"); + zassert_equal(DT_REG_SIZE(TEST_ALIAS), 0x1000, "reg[0] len"); + zassert_true(!strcmp(DT_LABEL(TEST_ALIAS), "TEST_GPIO_1"), "label"); + zassert_equal(DT_PROP(TEST_ALIAS, gpio_controller), 1, + "gpio-controller"); + zassert_equal(DT_PROP(TEST_ALIAS, ngpios), 32, "ngpios"); + zassert_true(!strcmp(DT_PROP(TEST_ALIAS, status), "okay"), "status"); + zassert_equal(DT_PROP_LEN(TEST_ALIAS, compatible), 1, + "compatible len"); + zassert_true(!strcmp(DT_PROP_BY_IDX(TEST_ALIAS, compatible, 0), + "vnd,gpio"), + "compatible[0]"); +} + +static void test_nodelabel_props(void) +{ + zassert_equal(DT_NUM_REGS(TEST_NODELABEL), 1, "num regs"); + zassert_equal(DT_REG_ADDR(TEST_NODELABEL), 0xdeadbeef, "reg[0] addr"); + zassert_equal(DT_REG_SIZE(TEST_NODELABEL), 0x1000, "reg[0] len"); + zassert_true(!strcmp(DT_LABEL(TEST_NODELABEL), "TEST_GPIO_1"), "label"); + zassert_equal(DT_PROP(TEST_NODELABEL, gpio_controller), 1, + "gpio-controller"); + zassert_equal(DT_PROP(TEST_NODELABEL, ngpios), 32, "ngpios"); + zassert_true(!strcmp(DT_PROP(TEST_NODELABEL, status), "okay"), + "status"); + zassert_equal(DT_PROP_LEN(TEST_NODELABEL, compatible), 1, + "compatible len"); + zassert_true(!strcmp(DT_PROP_BY_IDX(TEST_NODELABEL, compatible, 0), + "vnd,gpio"), + "compatible[0]"); +} + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_gpio +static void test_inst_props(void) +{ + const char *label_startswith = "TEST_GPIO_"; + + /* + * Careful: + * + * We can only test properties that are shared across all + * instances of this compatible here. + */ + + zassert_equal(DT_PROP(TEST_INST, gpio_controller), 1, + "gpio-controller"); + zassert_true(!strcmp(DT_PROP(TEST_INST, status), "okay"), + "status"); + zassert_equal(DT_PROP_LEN(TEST_INST, compatible), 1, + "compatible len"); + zassert_true(!strcmp(DT_PROP_BY_IDX(TEST_INST, compatible, 0), + "vnd,gpio"), + "compatible[0]"); + + zassert_equal(DT_INST_NODE_HAS_PROP(0, gpio_controller), 1, + "inst 0 has no gpio-controller"); + zassert_equal(DT_INST_PROP(0, gpio_controller), 1, + "inst 0 gpio-controller is not 1"); + zassert_equal(DT_INST_NODE_HAS_PROP(0, xxxx), 0, + "inst 0 has xxxx prop"); + zassert_true(!strcmp(DT_INST_PROP(0, status), "okay"), + "inst 0 status"); + zassert_equal(DT_INST_PROP_LEN(0, compatible), 1, + "inst 0 compatible len"); + zassert_true(!strcmp(DT_INST_PROP_BY_IDX(0, compatible, 0), + "vnd,gpio"), + "inst 0 compatible[0]"); + zassert_true(!strncmp(label_startswith, DT_INST_LABEL(0), + strlen(label_startswith)), + "inst 0 label"); +} + +static void test_has_path(void) +{ + zassert_equal(DT_HAS_NODE(DT_PATH(test, gpio_0)), 0, "gpio@0"); + zassert_equal(DT_HAS_NODE(DT_PATH(test, gpio_deadbeef)), 1, + "gpio@deadbeef"); + zassert_equal(DT_HAS_NODE(DT_PATH(test, gpio_abcd1234)), 1, + "gpio@abcd1234"); +} + +static void test_has_alias(void) +{ + zassert_equal(DT_HAS_NODE(DT_ALIAS(test_alias)), 1, "test-alias"); + zassert_equal(DT_HAS_NODE(DT_ALIAS(test_undef)), 0, "test-undef"); +} + +static void test_has_inst(void) +{ + zassert_equal(DT_HAS_NODE(DT_INST(0, vnd_gpio)), 1, "vnd,gpio #0"); + zassert_equal(DT_HAS_NODE(DT_INST(1, vnd_gpio)), 1, "vnd,gpio #1"); + zassert_equal(DT_HAS_NODE(DT_INST(2, vnd_gpio)), 0, "vnd,gpio #2"); +} + +static void test_has_nodelabel(void) +{ + zassert_equal(DT_HAS_NODE(DT_NODELABEL(disabled_gpio)), 0, + "disabled_gpio"); + zassert_equal(DT_HAS_NODE(DT_NODELABEL(test_nodelabel)), 1, + "test_nodelabel"); + zassert_equal(DT_HAS_NODE(DT_NODELABEL(test_nodelabel_allcaps)), 1, + "TEST_NODELABEL_ALLCAPS"); +} + +#define TA_HAS_COMPAT(compat) DT_NODE_HAS_COMPAT(TEST_ARRAYS, compat) + +static void test_has_compat(void) +{ + unsigned int compats; + + zassert_true(DT_HAS_COMPAT(vnd_gpio), "vnd,gpio"); + zassert_true(DT_HAS_COMPAT(vnd_gpio), "vnd,i2c"); + zassert_false(DT_HAS_COMPAT(vnd_disabled_compat), + "vnd,disabled-compat"); + + zassert_equal(TA_HAS_COMPAT(vnd_array_holder), 1, "vnd,array-holder"); + zassert_equal(TA_HAS_COMPAT(vnd_undefined_compat), 1, + "vnd,undefined-compat"); + zassert_equal(TA_HAS_COMPAT(vnd_not_a_test_array_compat), 0, + "not present"); + compats = ((TA_HAS_COMPAT(vnd_array_holder) << 0) | + (TA_HAS_COMPAT(vnd_undefined_compat) << 1) | + (TA_HAS_COMPAT(vnd_not_a_test_array_compat) << 2)); + zassert_equal(compats, 0x3, "as bit array"); +} + +#define TEST_I2C_DEV DT_PATH(test, i2c_11112222, test_i2c_dev_10) +#define TEST_I2C_BUS DT_BUS(TEST_I2C_DEV) + +#define TEST_SPI DT_NODELABEL(test_spi) + +#define TEST_SPI_DEV_0 DT_PATH(test, spi_33334444, test_spi_dev_0) +#define TEST_SPI_BUS_0 DT_BUS(TEST_SPI_DEV_0) + +#define TEST_SPI_DEV_1 DT_PATH(test, spi_33334444, test_spi_dev_1) +#define TEST_SPI_BUS_1 DT_BUS(TEST_SPI_DEV_1) + +#define TEST_SPI_NO_CS DT_NODELABEL(test_spi_no_cs) +#define TEST_SPI_DEV_NO_CS DT_NODELABEL(test_spi_no_cs) + +static void test_bus(void) +{ + /* common prefixes of expected labels: */ + const char *i2c_bus = "TEST_I2C_CTLR"; + const char *i2c_dev = "TEST_I2C_DEV"; + const char *spi_bus = "TEST_SPI_CTLR"; + const char *spi_dev = "TEST_SPI_DEV"; + const char *gpio = "TEST_GPIO_"; + int pin, flags; + + zassert_true(!strcmp(DT_LABEL(TEST_I2C_BUS), "TEST_I2C_CTLR"), "i2c"); + zassert_true(!strcmp(DT_LABEL(TEST_SPI_BUS_0), "TEST_SPI_CTLR"), + "spi 0"); + zassert_true(!strcmp(DT_LABEL(TEST_SPI_BUS_1), "TEST_SPI_CTLR"), + "spi 1"); + + zassert_equal(DT_SPI_DEV_HAS_CS(TEST_SPI_DEV_0), 1, "no cs"); + zassert_equal(DT_SPI_DEV_HAS_CS(TEST_SPI_DEV_NO_CS), 0, "has cs"); + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_spi_device_2 + /* there is only one instance, and it has no CS */ + zassert_equal(DT_INST_SPI_DEV_HAS_CS(0), 0, + "inst of vnd,spi-device-2 with cs"); + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_spi_device + /* + * DT_INST_SPI_DEV: use with care here. We could be matching + * either vnd,spi-device. + */ + zassert_equal(DT_INST_SPI_DEV_HAS_CS(0), 1, + "inst of vnd,spi-device without cs"); + + zassert_true(!strncmp(gpio, DT_INST_SPI_DEV_CS_GPIO_LABEL(0), + strlen(gpio)), + "inst 0 cs label"); + + pin = DT_INST_SPI_DEV_CS_GPIO_PIN(0); + zassert_true((pin == 0x10) || (pin == 0x30), "inst 0 cs pin"); + + flags = DT_INST_SPI_DEV_CS_GPIO_FLAGS(0); + zassert_true((flags == 0x20) || (flags == 0x40), "inst 0 cs flags"); + + zassert_equal(DT_ON_BUS(TEST_SPI_DEV_0, spi), 1, "spidev not on spi"); + zassert_equal(DT_ON_BUS(TEST_SPI_DEV_0, i2c), 0, "spidev on i2c"); + + zassert_equal(DT_ON_BUS(TEST_I2C_DEV, i2c), 1, "i2cdev not on i2c"); + zassert_equal(DT_ON_BUS(TEST_I2C_DEV, spi), 0, "i2cdev on spi"); + + zassert_true(!strcmp(DT_BUS_LABEL(TEST_I2C_DEV), "TEST_I2C_CTLR"), + "bus label"); + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_spi_device + zassert_equal(DT_INST_ON_BUS(0, spi), 1, "spi inst 0 not on spi"); + zassert_equal(DT_INST_ON_BUS(0, i2c), 0, "spi inst 0 on i2c"); + + zassert_equal(DT_ANY_INST_ON_BUS(spi), 1, "no spi is on spi"); + zassert_equal(DT_ANY_INST_ON_BUS(i2c), 0, "a spi is on i2c"); + + zassert_true(!strncmp(spi_dev, DT_INST_LABEL(0), strlen(spi_dev)), + "inst 0 spi dev label"); + zassert_true(!strncmp(spi_bus, DT_INST_BUS_LABEL(0), strlen(spi_bus)), + "inst 0 spi bus label"); + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_i2c_device + zassert_equal(DT_INST_ON_BUS(0, i2c), 1, "i2c inst 0 not on i2c"); + zassert_equal(DT_INST_ON_BUS(0, spi), 0, "i2c inst 0 on spi"); + + zassert_equal(DT_ANY_INST_ON_BUS(i2c), 1, "no i2c is on i2c"); + zassert_equal(DT_ANY_INST_ON_BUS(spi), 0, "an i2c is on spi"); + + zassert_true(!strncmp(i2c_dev, DT_INST_LABEL(0), strlen(i2c_dev)), + "inst 0 i2c dev label"); + zassert_true(!strncmp(i2c_bus, DT_INST_BUS_LABEL(0), strlen(i2c_bus)), + "inst 0 i2c bus label"); +} + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_reg_holder +static void test_reg(void) +{ + /* DT_REG_HAS_IDX */ + zassert_true(DT_REG_HAS_IDX(TEST_ABCD1234, 0), "has idx 0"); + zassert_true(DT_REG_HAS_IDX(TEST_ABCD1234, 1), "has idx 1"); + zassert_false(DT_REG_HAS_IDX(TEST_ABCD1234, 2), "!has idx 2"); + + /* DT_REG_ADDR_BY_IDX */ + zassert_equal(DT_REG_ADDR_BY_IDX(TEST_ABCD1234, 0), 0xabcd1234, + "abcd1234 reg[1] addr"); + zassert_equal(DT_REG_ADDR_BY_IDX(TEST_ABCD1234, 1), 0x98765432, + "abcd1234 reg[1] addr"); + + /* DT_REG_SIZE_BY_IDX */ + zassert_equal(DT_REG_SIZE_BY_IDX(TEST_ABCD1234, 0), 0x500, + "abcd1234 reg[1] size"); + zassert_equal(DT_REG_SIZE_BY_IDX(TEST_ABCD1234, 1), 0xff, + "abcd1234 reg[1] size"); + + /* DT_REG_ADDR */ + zassert_equal(DT_REG_ADDR(TEST_ABCD1234), 0xabcd1234, + "abcd1234 reg[0] addr"); + + /* DT_REG_SIZE */ + zassert_equal(DT_REG_SIZE(TEST_ABCD1234), 0x500, + "abcd1234 reg[0] size"); + + /* DT_REG_ADDR_BY_NAME */ + zassert_equal(DT_REG_ADDR_BY_NAME(TEST_ABCD1234, one), 0xabcd1234, + "abcd1234 reg[one] addr"); + zassert_equal(DT_REG_ADDR_BY_NAME(TEST_ABCD1234, two), 0x98765432, + "abcd1234 reg[two] addr"); + + /* DT_REG_SIZE_BY_NAME */ + zassert_equal(DT_REG_SIZE_BY_NAME(TEST_ABCD1234, one), 0x500, + "abcd1234 reg[one] size"); + zassert_equal(DT_REG_SIZE_BY_NAME(TEST_ABCD1234, two), 0xff, + "abcd1234 reg[two] size"); + + /* DT_INST */ + zassert_equal(DT_NUM_INST(DT_DRV_COMPAT), 1, "one instance"); + + /* DT_INST_REG_HAS_IDX */ + zassert_true(DT_INST_REG_HAS_IDX(0, 0), "has idx 0"); + zassert_true(DT_INST_REG_HAS_IDX(0, 1), "has idx 1"); + zassert_false(DT_INST_REG_HAS_IDX(0, 2), "!has idx 2"); + + /* DT_INST_REG_ADDR_BY_IDX */ + zassert_equal(DT_INST_REG_ADDR_BY_IDX(0, 0), 0x9999aaaa, + "abcd1234 reg[1] addr"); + zassert_equal(DT_INST_REG_ADDR_BY_IDX(0, 1), 0xbbbbcccc, + "abcd1234 reg[1] addr"); + + /* DT_INST_REG_SIZE_BY_IDX */ + zassert_equal(DT_INST_REG_SIZE_BY_IDX(0, 0), 0x1000, + "abcd1234 reg[1] size"); + zassert_equal(DT_INST_REG_SIZE_BY_IDX(0, 1), 0x3f, + "abcd1234 reg[1] size"); + + /* DT_INST_REG_ADDR */ + zassert_equal(DT_INST_REG_ADDR(0), 0x9999aaaa, + "abcd1234 reg[0] addr"); + + /* DT_INST_REG_SIZE */ + zassert_equal(DT_INST_REG_SIZE(0), 0x1000, + "abcd1234 reg[0] size"); + + /* DT_INST_REG_ADDR_BY_NAME */ + zassert_equal(DT_INST_REG_ADDR_BY_NAME(0, first), 0x9999aaaa, + "abcd1234 reg[one] addr"); + zassert_equal(DT_INST_REG_ADDR_BY_NAME(0, second), 0xbbbbcccc, + "abcd1234 reg[two] addr"); + + /* DT_INST_REG_SIZE_BY_NAME */ + zassert_equal(DT_INST_REG_SIZE_BY_NAME(0, first), 0x1000, + "abcd1234 reg[one] size"); + zassert_equal(DT_INST_REG_SIZE_BY_NAME(0, second), 0x3f, + "abcd1234 reg[two] size"); +} + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_interrupt_holder +static void test_irq(void) +{ + /* DT_NUM_IRQS */ + zassert_equal(DT_NUM_IRQS(TEST_DEADBEEF), 1, "deadbeef num_irqs"); + zassert_equal(DT_NUM_IRQS(TEST_I2C_BUS), 2, "TEST_I2C_BUS num_irqs"); + zassert_equal(DT_NUM_IRQS(TEST_SPI), 3, "TEST_SPI num_irqs"); + + /* DT_IRQ_HAS_IDX */ + zassert_true(DT_IRQ_HAS_IDX(TEST_SPI_BUS_0, 0), "spi has idx 0"); + zassert_true(DT_IRQ_HAS_IDX(TEST_SPI_BUS_0, 1), "spi has idx 1"); + zassert_true(DT_IRQ_HAS_IDX(TEST_SPI_BUS_0, 2), "spi has idx 2"); + zassert_false(DT_IRQ_HAS_IDX(TEST_SPI_BUS_0, 3), "!spi has idx 3"); + + zassert_true(DT_IRQ_HAS_IDX(TEST_DEADBEEF, 0), "deadbeef has idx 0"); + zassert_false(DT_IRQ_HAS_IDX(TEST_DEADBEEF, 1), "!deadbeef has idx 1"); + + zassert_true(DT_IRQ_HAS_IDX(TEST_I2C_BUS, 0), "i2c has idx 0"); + zassert_true(DT_IRQ_HAS_IDX(TEST_I2C_BUS, 1), "i2c has idx 1"); + zassert_false(DT_IRQ_HAS_IDX(TEST_I2C_BUS, 2), "!i2c has idx 2"); + + /* DT_IRQ_BY_IDX */ + zassert_equal(DT_IRQ_BY_IDX(TEST_SPI_BUS_0, 0, irq), 8, "irq 0"); + zassert_equal(DT_IRQ_BY_IDX(TEST_SPI_BUS_0, 1, irq), 9, "irq 1"); + zassert_equal(DT_IRQ_BY_IDX(TEST_SPI_BUS_0, 2, irq), 10, "irq 2"); + zassert_equal(DT_IRQ_BY_IDX(TEST_SPI_BUS_0, 0, priority), 3, "irq 0 pri"); + zassert_equal(DT_IRQ_BY_IDX(TEST_SPI_BUS_0, 1, priority), 0, "irq 1 pri"); + zassert_equal(DT_IRQ_BY_IDX(TEST_SPI_BUS_0, 2, priority), 1, "irq 2 pri"); + + /* DT_IRQ_BY_NAME */ + zassert_equal(DT_IRQ_BY_NAME(TEST_I2C_BUS, status, irq), 6, "irq status"); + zassert_equal(DT_IRQ_BY_NAME(TEST_I2C_BUS, error, irq), 7, "irq error"); + zassert_equal(DT_IRQ_BY_NAME(TEST_I2C_BUS, status, priority), 2, + "irq status pri"); + zassert_equal(DT_IRQ_BY_NAME(TEST_I2C_BUS, error, priority), 1, + "irq error pri"); + + /* DT_IRQ_HAS_CELL_AT_IDX */ + zassert_true(DT_IRQ_HAS_CELL_AT_IDX(TEST_IRQ, 0, irq), "no irq 0"); + zassert_true(DT_IRQ_HAS_CELL_AT_IDX(TEST_IRQ, 0, priority), + "no irq 0 pri"); + zassert_false(DT_IRQ_HAS_CELL_AT_IDX(TEST_IRQ, 0, foo), 0, + "has irq 0 foo"); + zassert_true(DT_IRQ_HAS_CELL_AT_IDX(TEST_IRQ, 2, irq), "no irq 2"); + zassert_true(DT_IRQ_HAS_CELL_AT_IDX(TEST_IRQ, 2, priority), + "no irq 2 pri"); + zassert_false(DT_IRQ_HAS_CELL_AT_IDX(TEST_IRQ, 2, foo), + "has irq 2 foo"); + + /* DT_IRQ_HAS_CELL */ + zassert_true(DT_IRQ_HAS_CELL(TEST_IRQ, irq), "no irq"); + zassert_true(DT_IRQ_HAS_CELL(TEST_IRQ, priority), "no irq pri"); + zassert_false(DT_IRQ_HAS_CELL(TEST_IRQ, foo), "has irq foo"); + + /* DT_IRQ_HAS_NAME */ + zassert_true(DT_IRQ_HAS_NAME(TEST_IRQ, err), "no err irq"); + zassert_true(DT_IRQ_HAS_NAME(TEST_IRQ, stat), "no stat irq"); + zassert_true(DT_IRQ_HAS_NAME(TEST_IRQ, done), "no done irq"); + zassert_false(DT_IRQ_HAS_NAME(TEST_IRQ, alpha), "no err irq"); + + /* DT_IRQ */ + zassert_equal(DT_IRQ(TEST_I2C_BUS, irq), 6, "irq"); + zassert_equal(DT_IRQ(TEST_I2C_BUS, priority), 2, "irq pri"); + + /* DT_IRQN */ + zassert_equal(DT_IRQN(TEST_I2C_BUS), 6, "irqn"); + + /* DT_INST */ + zassert_equal(DT_NUM_INST(DT_DRV_COMPAT), 1, "one instance"); + + /* DT_INST_IRQ_HAS_IDX */ + zassert_equal(DT_INST_IRQ_HAS_IDX(0, 0), 1, "inst 0 irq 0 missing"); + zassert_equal(DT_INST_IRQ_HAS_IDX(0, 1), 1, "inst 0 irq 1 missing"); + zassert_equal(DT_INST_IRQ_HAS_IDX(0, 2), 1, "inst 0 irq 2 missing"); + zassert_equal(DT_INST_IRQ_HAS_IDX(0, 3), 0, "inst 0 irq 3 present"); + + /* DT_INST_IRQ_BY_IDX */ + zassert_equal(DT_INST_IRQ_BY_IDX(0, 0, irq), 30, "inst 0 irq 0"); + zassert_equal(DT_INST_IRQ_BY_IDX(0, 1, irq), 40, "inst 0 irq 1"); + zassert_equal(DT_INST_IRQ_BY_IDX(0, 2, irq), 60, "inst 0 irq 2"); + zassert_equal(DT_INST_IRQ_BY_IDX(0, 0, priority), 3, "inst 0 irq 0 pri"); + zassert_equal(DT_INST_IRQ_BY_IDX(0, 1, priority), 5, "inst 0 irq 1 pri"); + zassert_equal(DT_INST_IRQ_BY_IDX(0, 2, priority), 7, "inst 0 irq 2 pri"); + + /* DT_INST_IRQ_BY_NAME */ + zassert_equal(DT_INST_IRQ_BY_NAME(0, err, irq), 30, "inst 0 irq error"); + zassert_equal(DT_INST_IRQ_BY_NAME(0, stat, irq), 40, "inst 0 irq status"); + zassert_equal(DT_INST_IRQ_BY_NAME(0, done, irq), 60, "inst 0 done error"); + zassert_equal(DT_INST_IRQ_BY_NAME(0, err, priority), 3, + "inst 0 irq error pri"); + zassert_equal(DT_INST_IRQ_BY_NAME(0, stat, priority), 5, + "inst 0 irq status pri"); + zassert_equal(DT_INST_IRQ_BY_NAME(0, done, priority), 7, + "inst 0 irq done pri"); + + /* DT_INST_IRQ */ + zassert_equal(DT_INST_IRQ(0, irq), 30, "inst 0 irq"); + zassert_equal(DT_INST_IRQ(0, priority), 3, "inst 0 irq pri"); + + /* DT_INST_IRQN */ + zassert_equal(DT_INST_IRQN(0), 30, "inst 0 irqn"); + + /* DT_INST_IRQ_HAS_CELL_AT_IDX */ + zassert_true(DT_INST_IRQ_HAS_CELL_AT_IDX(0, 0, irq), "no inst irq 0"); + zassert_true(DT_INST_IRQ_HAS_CELL_AT_IDX(0, 0, priority), + "no inst irq 0 pri"); + zassert_false(DT_INST_IRQ_HAS_CELL_AT_IDX(0, 0, foo), + "has inst irq 0 foo"); + zassert_true(DT_INST_IRQ_HAS_CELL_AT_IDX(0, 2, irq), "no inst irq 2"); + zassert_true(DT_INST_IRQ_HAS_CELL_AT_IDX(0, 2, priority), + "no inst irq 2 pri"); + zassert_false(DT_INST_IRQ_HAS_CELL_AT_IDX(0, 2, foo), + "has inst irq 2 foo"); + + /* DT_INST_IRQ_HAS_CELL */ + zassert_true(DT_INST_IRQ_HAS_CELL(0, irq), "no inst 0 irq"); + zassert_true(DT_INST_IRQ_HAS_CELL(0, priority), "no inst 0 irq pri"); + zassert_false(DT_INST_IRQ_HAS_CELL(0, foo), "has inst 0 irq foo"); + + /* DT_INST_IRQ_HAS_NAME */ + zassert_true(DT_INST_IRQ_HAS_NAME(0, err), "no inst err irq"); + zassert_true(DT_INST_IRQ_HAS_NAME(0, stat), "no inst err irq"); + zassert_true(DT_INST_IRQ_HAS_NAME(0, done), "no inst err irq"); + zassert_false(DT_INST_IRQ_HAS_NAME(0, alpha), "no inst err irq"); + +} + +struct gpios_struct { + const char *name; + gpio_pin_t pin; + gpio_flags_t flags; +}; + +/* Helper macro that UTIL_LISTIFY can use and produces an element with comma */ +#define DT_PROP_ELEM_BY_PHANDLE(idx, node_id, ph_prop, prop) \ + DT_PROP_BY_PHANDLE_IDX(node_id, ph_prop, idx, prop), +#define DT_PHANDLE_LISTIFY(node_id, ph_prop, prop) \ + { \ + UTIL_LISTIFY(DT_PROP_LEN(node_id, ph_prop), \ + DT_PROP_ELEM_BY_PHANDLE, \ + node_id, \ + ph_prop, \ + label) \ + } + +/* Helper macro that UTIL_LISTIFY can use and produces an element with comma */ +#define DT_GPIO_ELEM(idx, node_id, prop) \ + { \ + DT_PROP(DT_PHANDLE_BY_IDX(node_id, prop, idx), label), \ + DT_PHA_BY_IDX(node_id, prop, idx, pin),\ + DT_PHA_BY_IDX(node_id, prop, idx, flags),\ + }, +#define DT_GPIO_LISTIFY(node_id, prop) \ + { UTIL_LISTIFY(DT_PROP_LEN(node_id, prop), DT_GPIO_ELEM, \ + node_id, prop) } + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_phandle_holder +static void test_phandles(void) +{ + const char *ph_label = DT_PROP_BY_PHANDLE(TEST_PH, ph, label); + const char *phs_labels[] = DT_PHANDLE_LISTIFY(TEST_PH, phs, label); + struct gpios_struct gps[] = DT_GPIO_LISTIFY(TEST_PH, gpios); + + /* phandle */ + zassert_true(DT_NODE_HAS_PROP(TEST_PH, ph), "ph"); + /* DT_PROP_BY_PHANDLE */ + zassert_true(!strcmp(ph_label, "TEST_GPIO_1"), "ph label"); + + /* phandles */ + zassert_true(DT_NODE_HAS_PROP(TEST_PH, phs), "phs"); + zassert_equal(ARRAY_SIZE(phs_labels), 3, "phs size"); + zassert_equal(DT_PROP_LEN(TEST_PH, phs), 3, "phs len"); + + /* DT_PROP_BY_PHANDLE_IDX */ + zassert_true(!strcmp(DT_PROP_BY_PHANDLE_IDX(TEST_PH, phs, 0, label), + "TEST_GPIO_1"), "phs 0"); + zassert_true(!strcmp(DT_PROP_BY_PHANDLE_IDX(TEST_PH, phs, 1, label), + "TEST_GPIO_2"), "phs 1"); + zassert_true(!strcmp(DT_PROP_BY_PHANDLE_IDX(TEST_PH, phs, 2, label), + "TEST_I2C_CTLR"), "phs 2"); + zassert_true(!strcmp(phs_labels[0], "TEST_GPIO_1"), "phs_labels[0]"); + zassert_true(!strcmp(phs_labels[1], "TEST_GPIO_2"), "phs_labels[1]"); + zassert_true(!strcmp(phs_labels[2], "TEST_I2C_CTLR"), "phs_labels[2]"); + zassert_equal(DT_PROP_BY_PHANDLE_IDX(TEST_PH, gpios, 0, + gpio_controller), + 1, + "gpios[0].gpio_controller"); + zassert_equal(DT_PROP_BY_PHANDLE_IDX(TEST_PH, gpios, 1, + gpio_controller), + 1, + "gpios[1].gpio_controller"); + zassert_true(!strcmp(DT_PROP_BY_PHANDLE_IDX(TEST_PH, gpios, 0, label), + "TEST_GPIO_1"), + "gpios[0].label"); + zassert_true(!strcmp(DT_PROP_BY_PHANDLE_IDX(TEST_PH, gpios, 1, label), + "TEST_GPIO_2"), + "gpios[1].label"); + + /* phandle-array */ + zassert_true(DT_NODE_HAS_PROP(TEST_PH, gpios), "gpios"); + zassert_equal(ARRAY_SIZE(gps), 2, "gpios size"); + zassert_equal(DT_PROP_LEN(TEST_PH, gpios), 2, "pha len"); + + /* DT_PHA_HAS_IDX */ + zassert_true(DT_PROP_HAS_IDX(TEST_PH, gpios, 0), "has idx 0"); + zassert_true(DT_PROP_HAS_IDX(TEST_PH, gpios, 1), "has idx 1"); + zassert_false(DT_PROP_HAS_IDX(TEST_PH, gpios, 2), "!has idx 2"); + + /* DT_PHA_HAS_CELL_AT_IDX */ + zassert_true(DT_PHA_HAS_CELL_AT_IDX(TEST_PH, gpios, 1, pin), + "has gpio 1 pin"); + zassert_true(DT_PHA_HAS_CELL_AT_IDX(TEST_PH, gpios, 1, flags), + "has gpio 1 flags"); + zassert_true(DT_PHA_HAS_CELL_AT_IDX(TEST_PH, pha_gpios, 1, pin), + "no pha-gpio 1 pin"); + /* index 1 only has pin, no flags */ + zassert_false(DT_PHA_HAS_CELL_AT_IDX(TEST_PH, pha_gpios, 1, flags), + "no pha-gpio 1 flags"); + zassert_true(DT_PHA_HAS_CELL_AT_IDX(TEST_PH, pha_gpios, 2, pin), + "no pha-gpio 2 pin"); + zassert_true(DT_PHA_HAS_CELL_AT_IDX(TEST_PH, pha_gpios, 2, flags), + "no pha-gpio 2 flags"); + + /* DT_PHA_HAS_CELL */ + zassert_true(DT_PHA_HAS_CELL(TEST_PH, gpios, flags), "has gpio flags"); + zassert_false(DT_PHA_HAS_CELL(TEST_PH, gpios, bar), "no gpio bar"); + + /* DT_PHANDLE_BY_IDX */ + zassert_true(!strcmp(DT_LABEL(DT_PHANDLE_BY_IDX(TEST_PH, gpios, 0)), + "TEST_GPIO_1"), + "gpios 0"); + zassert_true(!strcmp(DT_LABEL(DT_PHANDLE_BY_IDX(TEST_PH, gpios, 1)), + "TEST_GPIO_2"), + "gpios 1"); + + /* DT_PHANDLE */ + zassert_true(!strcmp(DT_LABEL(DT_PHANDLE(TEST_PH, gpios)), + "TEST_GPIO_1"), + "gpios"); + + /* DT_PHA */ + zassert_equal(DT_PHA(TEST_PH, gpios, pin), 10, "pin 0 implicit"); + zassert_equal(DT_PHA(TEST_PH, gpios, flags), 20, "flags 0 implicit"); + + /* DT_PHA_BY_IDX */ + zassert_equal(DT_PHA_BY_IDX(TEST_PH, gpios, 0, pin), 10, "pin 0"); + zassert_equal(DT_PHA_BY_IDX(TEST_PH, gpios, 0, flags), 20, "flags 0"); + + zassert_equal(DT_PHA_BY_IDX(TEST_PH, gpios, 1, pin), 30, "pin 1"); + zassert_equal(DT_PHA_BY_IDX(TEST_PH, gpios, 1, flags), 40, "flags 1"); + + /* DT_PHA_BY_NAME */ + zassert_equal(DT_PHA_BY_NAME(TEST_PH, foos, a, foocell), 100, + "foocell A"); + zassert_equal(DT_PHA_BY_NAME(TEST_PH, foos, b_c, foocell), 110, + "foocell b-c"); + + /* DT_PHANDLE_BY_NAME */ + zassert_true(!strcmp(DT_LABEL(DT_PHANDLE_BY_NAME(TEST_PH, foos, a)), + "TEST_GPIO_1"), + "phandle A"); + zassert_true(!strcmp(DT_LABEL(DT_PHANDLE_BY_NAME(TEST_PH, foos, b_c)), + "TEST_GPIO_2"), + "phandle b-c"); + + /* array initializers */ + zassert_true(!strcmp(gps[0].name, "TEST_GPIO_1"), "gps[0].name"); + zassert_equal(gps[0].pin, 10, "gps[0].pin"); + zassert_equal(gps[0].flags, 20, "gps[0].flags"); + + zassert_true(!strcmp(gps[1].name, "TEST_GPIO_2"), "gps[1].name"); + zassert_equal(gps[1].pin, 30, "gps[1].pin"); + zassert_equal(gps[1].flags, 40, "gps[1].flags"); + + /* DT_INST */ + zassert_equal(DT_NUM_INST(DT_DRV_COMPAT), 1, "one instance"); + + /* DT_INST_PROP_BY_PHANDLE */ + zassert_true(!strcmp(DT_INST_PROP_BY_PHANDLE(0, ph, label), + "TEST_GPIO_1"), "ph label"); + + zassert_true(!strcmp(ph_label, "TEST_GPIO_1"), "ph label"); + + /* DT_INST_PROP_BY_PHANDLE_IDX */ + zassert_true(!strcmp(DT_INST_PROP_BY_PHANDLE_IDX(0, phs, 0, label), + "TEST_GPIO_1"), "phs 0"); + zassert_true(!strcmp(DT_INST_PROP_BY_PHANDLE_IDX(0, phs, 1, label), + "TEST_GPIO_2"), "phs 1"); + zassert_true(!strcmp(DT_INST_PROP_BY_PHANDLE_IDX(0, phs, 2, label), + "TEST_I2C_CTLR"), "phs 2"); + zassert_true(!strcmp(phs_labels[0], "TEST_GPIO_1"), "phs_labels[0]"); + zassert_true(!strcmp(phs_labels[1], "TEST_GPIO_2"), "phs_labels[1]"); + zassert_true(!strcmp(phs_labels[2], "TEST_I2C_CTLR"), "phs_labels[2]"); + zassert_equal(DT_INST_PROP_BY_PHANDLE_IDX(0, gpios, 0, + gpio_controller), + 1, + "gpios[0].gpio_controller"); + zassert_equal(DT_INST_PROP_BY_PHANDLE_IDX(0, gpios, 1, + gpio_controller), + 1, + "gpios[1].gpio_controller"); + zassert_true(!strcmp(DT_INST_PROP_BY_PHANDLE_IDX(0, gpios, 0, label), + "TEST_GPIO_1"), + "gpios[0].label"); + zassert_true(!strcmp(DT_INST_PROP_BY_PHANDLE_IDX(0, gpios, 1, label), + "TEST_GPIO_2"), + "gpios[1].label"); + +#if 0 + /* DT_INST_PHA_HAS_IDX */ + zassert_true(DT_INST_PROP_HAS_IDX(0, gpios, 0), "has idx 0"); + zassert_true(DT_INST_PROP_HAS_IDX(0, gpios, 1), "has idx 1"); + zassert_false(DT_INST_PROP_HAS_IDX(0, gpios, 2), "!has idx 2"); + + /* DT_INST_PHA_HAS_CELL_AT_IDX */ + zassert_true(DT_INST_PHA_HAS_CELL_AT_IDX(0, gpios, 1, pin), + "has gpio 1 pin"); + zassert_true(DT_INST_PHA_HAS_CELL_AT_IDX(0, gpios, 1, flags), + "has gpio 1 flags"); + zassert_true(DT_INST_PHA_HAS_CELL_AT_IDX(0, pha_gpios, 1, pin), + "no pha-gpio 1 pin"); + /* index 1 only has pin, no flags */ + zassert_false(DT_INST_PHA_HAS_CELL_AT_IDX(0, pha_gpios, 1, flags), + "no pha-gpio 1 flags"); + zassert_true(DT_INST_PHA_HAS_CELL_AT_IDX(0, pha_gpios, 2, pin), + "no pha-gpio 2 pin"); + zassert_true(DT_INST_PHA_HAS_CELL_AT_IDX(0, pha_gpios, 2, flags), + "no pha-gpio 2 flags"); + + /* DT_INST_PHA_HAS_CELL */ + zassert_true(DT_INST_PHA_HAS_CELL(0, gpios, flags), "has gpio flags"); + zassert_false(DT_INST_PHA_HAS_CELL(0, gpios, bar), "no gpio bar"); +#endif + + /* DT_INST_PHANDLE_BY_IDX */ + zassert_true(!strcmp(DT_LABEL(DT_INST_PHANDLE_BY_IDX(0, gpios, 0)), + "TEST_GPIO_1"), + "gpios 0"); + zassert_true(!strcmp(DT_LABEL(DT_INST_PHANDLE_BY_IDX(0, gpios, 1)), + "TEST_GPIO_2"), + "gpios 1"); + + /* DT_INST_PHANDLE */ + zassert_true(!strcmp(DT_LABEL(DT_INST_PHANDLE(0, gpios)), + "TEST_GPIO_1"), + "gpios"); + + /* DT_INST_PHA */ + zassert_equal(DT_INST_PHA(0, gpios, pin), 10, "pin 0 implicit"); + zassert_equal(DT_INST_PHA(0, gpios, flags), 20, "flags 0 implicit"); + + /* DT_INST_PHA_BY_IDX */ + zassert_equal(DT_INST_PHA_BY_IDX(0, gpios, 0, pin), 10, "pin 0"); + zassert_equal(DT_INST_PHA_BY_IDX(0, gpios, 0, flags), 20, "flags 0"); + + zassert_equal(DT_INST_PHA_BY_IDX(0, gpios, 1, pin), 30, "pin 1"); + zassert_equal(DT_INST_PHA_BY_IDX(0, gpios, 1, flags), 40, "flags 1"); + + /* DT_INST_PHA_BY_NAME */ + zassert_equal(DT_INST_PHA_BY_NAME(0, foos, a, foocell), 100, + "foocell A"); + zassert_equal(DT_INST_PHA_BY_NAME(0, foos, b_c, foocell), 110, + "foocell b-c"); + + /* DT_INST_PHANDLE_BY_NAME */ + zassert_true(!strcmp(DT_LABEL(DT_INST_PHANDLE_BY_NAME(0, foos, a)), + "TEST_GPIO_1"), + "phandle A"); + zassert_true(!strcmp(DT_LABEL(DT_INST_PHANDLE_BY_NAME(0, foos, b_c)), + "TEST_GPIO_2"), + "phandle b-c"); +} + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_phandle_holder +static void test_gpio(void) +{ + + /* DT_GPIO_LABEL_BY_IDX */ + zassert_true(!strcmp(DT_GPIO_LABEL_BY_IDX(TEST_PH, gpios, 0), + "TEST_GPIO_1"), + "gpio 0 name idx"); + zassert_true(!strcmp(DT_GPIO_LABEL_BY_IDX(TEST_PH, gpios, 1), + "TEST_GPIO_2"), + "gpio 1 name idx"); + + /* DT_GPIO_LABEL */ + zassert_true(!strcmp(DT_GPIO_LABEL(TEST_PH, gpios), "TEST_GPIO_1"), + "gpio 0 name"); + + /* DT_GPIO_PIN_BY_IDX */ + zassert_equal(DT_GPIO_PIN_BY_IDX(TEST_PH, gpios, 0), 10, "gpio 0 pin idx"); + zassert_equal(DT_GPIO_PIN_BY_IDX(TEST_PH, gpios, 1), 30, "gpio 1 pin idx"); + + /* DT_GPIO_PIN */ + zassert_equal(DT_GPIO_PIN(TEST_PH, gpios), 10, "gpio 0 pin"); + + /* DT_GPIO_FLAGS_BY_IDX */ + zassert_equal(DT_GPIO_FLAGS_BY_IDX(TEST_PH, gpios, 0), + 20, "gpio 0 flags idx"); + zassert_equal(DT_GPIO_FLAGS_BY_IDX(TEST_PH, gpios, 1), + 40, "gpio 1 flags idx"); + + /* DT_GPIO_FLAGS */ + zassert_equal(DT_GPIO_FLAGS(TEST_PH, gpios), 20, "gpio 0 flags"); + + /* DT_INST */ + zassert_equal(DT_NUM_INST(DT_DRV_COMPAT), 1, "one instance"); + + /* DT_INST_GPIO_LABEL_BY_IDX */ + zassert_true(!strcmp(DT_INST_GPIO_LABEL_BY_IDX(0, gpios, 0), + "TEST_GPIO_1"), + "gpio inst 0 name idx"); + zassert_true(!strcmp(DT_INST_GPIO_LABEL_BY_IDX(0, gpios, 1), + "TEST_GPIO_2"), + "gpio inst 1 name idx"); + + /* DT_INST_GPIO_LABEL */ + zassert_true(!strcmp(DT_INST_GPIO_LABEL(0, gpios), "TEST_GPIO_1"), + "gpio inst 0 name"); + + /* DT_INST_GPIO_PIN_BY_IDX */ + zassert_equal(DT_INST_GPIO_PIN_BY_IDX(0, gpios, 0), + 10, "gpio inst 0 pin idx"); + zassert_equal(DT_INST_GPIO_PIN_BY_IDX(0, gpios, 1), + 30, "gpio inst 1 pin idx"); + + /* DT_INST_GPIO_PIN */ + zassert_equal(DT_INST_GPIO_PIN(0, gpios), 10, "gpio inst 0 pin"); + + /* DT_INST_GPIO_FLAGS_BY_IDX */ + zassert_equal(DT_INST_GPIO_FLAGS_BY_IDX(0, gpios, 0), + 20, "gpio inst 0 flags idx"); + zassert_equal(DT_INST_GPIO_FLAGS_BY_IDX(0, gpios, 1), + 40, "gpio inst 1 flags idx"); + + /* DT_INST_GPIO_FLAGS */ + zassert_equal(DT_INST_GPIO_FLAGS(0, gpios), 20, "gpio inst 0 flags"); +} + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_adc_temp_sensor +static void test_io_channels(void) +{ + zassert_true(!strcmp(DT_IO_CHANNELS_LABEL_BY_IDX(TEST_TEMP, 0), + "TEST_ADC_1"), + "label 0"); + zassert_true(!strcmp(DT_IO_CHANNELS_LABEL_BY_IDX(TEST_TEMP, 1), + "TEST_ADC_2"), + "label 1"); + zassert_true(!strcmp(DT_IO_CHANNELS_LABEL_BY_NAME(TEST_TEMP, ch1), + "TEST_ADC_1"), + "label ch1"); + zassert_true(!strcmp(DT_IO_CHANNELS_LABEL_BY_NAME(TEST_TEMP, ch2), + "TEST_ADC_2"), + "label ch2"); + zassert_true(!strcmp(DT_IO_CHANNELS_LABEL(TEST_TEMP), + "TEST_ADC_1"), + "label 0 implicit"); + + zassert_true(!strcmp(DT_INST_IO_CHANNELS_LABEL_BY_IDX(0, 0), + "TEST_ADC_1"), + "inst label 0"); + zassert_true(!strcmp(DT_INST_IO_CHANNELS_LABEL_BY_IDX(0, 1), + "TEST_ADC_2"), + "inst label 1"); + zassert_true(!strcmp(DT_INST_IO_CHANNELS_LABEL_BY_NAME(0, ch1), + "TEST_ADC_1"), + "inst ch1"); + zassert_true(!strcmp(DT_INST_IO_CHANNELS_LABEL_BY_NAME(0, ch2), + "TEST_ADC_2"), + "inst ch2"); + zassert_true(!strcmp(DT_INST_IO_CHANNELS_LABEL(0), + "TEST_ADC_1"), + "inst ch1 implicit"); + + zassert_equal(DT_IO_CHANNELS_INPUT_BY_IDX(TEST_TEMP, 0), 10, "input 0"); + zassert_equal(DT_IO_CHANNELS_INPUT_BY_IDX(TEST_TEMP, 1), 20, "input 1"); + zassert_equal(DT_IO_CHANNELS_INPUT_BY_NAME(TEST_TEMP, ch1), 10, + "input ch1"); + zassert_equal(DT_IO_CHANNELS_INPUT_BY_NAME(TEST_TEMP, ch2), 20, + "input ch2"); + zassert_equal(DT_IO_CHANNELS_INPUT(TEST_TEMP), 10, "input 0 implicit"); + + zassert_equal(DT_INST_IO_CHANNELS_INPUT_BY_IDX(0, 0), 10, "input 0"); + zassert_equal(DT_INST_IO_CHANNELS_INPUT_BY_IDX(0, 1), 20, "input 1"); + zassert_equal(DT_INST_IO_CHANNELS_INPUT_BY_NAME(0, ch1), 10, "input ch1"); + zassert_equal(DT_INST_IO_CHANNELS_INPUT_BY_NAME(0, ch2), 20, "input ch2"); + zassert_equal(DT_INST_IO_CHANNELS_INPUT(0), 10, "input 0 implicit"); +} + +#define TO_STRING(x) TO_STRING_(x) +#define TO_STRING_(x) #x + +static void test_macro_names(void) +{ + /* white box */ + zassert_true(!strcmp(TO_STRING(DT_PATH(test, gpio_deadbeef)), + "DT_N_S_test_S_gpio_deadbeef"), + "path"); + zassert_true(!strcmp(TO_STRING(DT_ALIAS(test_alias)), + "DT_N_S_test_S_gpio_deadbeef"), + "alias"); + zassert_true(!strcmp(TO_STRING(DT_NODELABEL(test_nodelabel)), + "DT_N_S_test_S_gpio_deadbeef"), + "nodelabel"); + zassert_true(!strcmp(TO_STRING(DT_NODELABEL(test_nodelabel_allcaps)), + "DT_N_S_test_S_gpio_deadbeef"), + "nodelabel (all caps)"); +} + +static int a[] = DT_PROP(TEST_ARRAYS, a); +static unsigned char b[] = DT_PROP(TEST_ARRAYS, b); +static char *c[] = DT_PROP(TEST_ARRAYS, c); + +static void test_arrays(void) +{ + zassert_equal(ARRAY_SIZE(a), 3, "a size"); + zassert_equal(ARRAY_SIZE(b), 4, "b size"); + zassert_equal(ARRAY_SIZE(c), 2, "c size"); + + zassert_equal(a[0], 1000, "a[0]"); + zassert_equal(a[1], 2000, "a[1]"); + zassert_equal(a[2], 3000, "a[2]"); + + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, a, 0), "a idx 0"); + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, a, 1), "a idx 1"); + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, a, 2), "a idx 2"); + zassert_false(DT_PROP_HAS_IDX(TEST_ARRAYS, a, 3), "!a idx 3"); + + zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, a, 0), a[0], "a 0"); + zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, a, 1), a[1], "a 1"); + zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, a, 2), a[2], "a 2"); + + zassert_equal(DT_PROP_LEN(TEST_ARRAYS, a), 3, "a len"); + + zassert_equal(b[0], 0xaa, "b[0]"); + zassert_equal(b[1], 0xbb, "b[1]"); + zassert_equal(b[2], 0xcc, "b[2]"); + zassert_equal(b[3], 0xdd, "b[3]"); + + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, b, 0), "b idx 0"); + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, b, 1), "b idx 1"); + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, b, 2), "b idx 2"); + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, b, 3), "b idx 3"); + zassert_false(DT_PROP_HAS_IDX(TEST_ARRAYS, b, 4), "!b idx 4"); + + zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, b, 0), b[0], "b 0"); + zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, b, 1), b[1], "b 0"); + zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, b, 2), b[2], "b 0"); + zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, b, 3), b[3], "b 0"); + + zassert_equal(DT_PROP_LEN(TEST_ARRAYS, b), 4, "b len"); + + zassert_true(!strcmp(c[0], "bar"), "c[0]"); + zassert_true(!strcmp(c[1], "baz"), "c[1]"); + + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, c, 0), "c idx 0"); + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, c, 1), "c idx 1"); + zassert_false(DT_PROP_HAS_IDX(TEST_ARRAYS, c, 2), "!c idx 2"); + + zassert_true(!strcmp(DT_PROP_BY_IDX(TEST_ARRAYS, c, 0), c[0]), "c 0"); + zassert_true(!strcmp(DT_PROP_BY_IDX(TEST_ARRAYS, c, 1), c[1]), "c 1"); + + zassert_equal(DT_PROP_LEN(TEST_ARRAYS, c), 2, "c len"); +} + +struct test_gpio_info { + u32_t reg_addr; + u32_t reg_len; +}; + +struct test_gpio_data { + bool init_called; + bool is_gpio_ctlr; +}; + +static int test_gpio_init(struct device *dev) +{ + struct test_gpio_data *data = dev->driver_data; + + data->init_called = 1; + return 0; +} + +#define INST(num) DT_INST(num, vnd_gpio) + +static const struct gpio_driver_api test_api; + +#define TEST_GPIO_INIT(num) \ + static struct test_gpio_data gpio_data_##num = { \ + .is_gpio_ctlr = DT_PROP(INST(num), \ + gpio_controller), \ + }; \ + static const struct test_gpio_info gpio_info_##num = { \ + .reg_addr = DT_REG_ADDR(INST(num)), \ + .reg_len = DT_REG_SIZE(INST(num)), \ + }; \ + DEVICE_AND_API_INIT(test_gpio_dev_##num, \ + DT_LABEL(INST(num)), \ + test_gpio_init, \ + &gpio_data_##num, \ + &gpio_info_##num, \ + POST_KERNEL, \ + CONFIG_APPLICATION_INIT_PRIORITY, \ + &test_api) + +#if DT_HAS_NODE(INST(0)) +TEST_GPIO_INIT(0); +#endif +#if DT_HAS_NODE(INST(1)) +TEST_GPIO_INIT(1); +#endif + +static inline struct test_gpio_data *to_data(struct device *dev) +{ + return (struct test_gpio_data *)dev->driver_data; +} + +static inline const struct test_gpio_info *to_info(struct device *dev) +{ + return (struct test_gpio_info *)dev->config->config_info; +} + +static void test_devices(void) +{ + struct device *dev0; + struct device *dev1; + struct device *dev_abcd; + + zassert_true(DT_HAS_NODE(INST(0)), "inst 0 device"); + zassert_true(DT_HAS_NODE(INST(1)), "inst 1 device"); + zassert_false(DT_HAS_NODE(INST(2)), "inst 2 device"); + + zassert_equal(DT_NUM_INST(vnd_gpio), 2, "wrong number of gpio devs"); + + dev0 = device_get_binding(DT_LABEL(INST(0))); + dev1 = device_get_binding(DT_LABEL(INST(1))); + + zassert_not_null(dev0, "null device " DT_LABEL(INST(0))); + zassert_not_null(dev1, "null device " DT_LABEL(INST(1))); + + zassert_true(to_data(dev0)->is_gpio_ctlr, "dev0 not a gpio"); + zassert_true(to_data(dev1)->is_gpio_ctlr, "dev1 not a gpio"); + zassert_true(to_data(dev0)->init_called, "dev0 not initialized"); + zassert_true(to_data(dev1)->init_called, "dev1 not initialized"); + + dev_abcd = device_get_binding(DT_LABEL(TEST_ABCD1234)); + zassert_not_null(dev_abcd, "abcd"); + zassert_equal(to_info(dev_abcd)->reg_addr, 0xabcd1234, "abcd addr"); + zassert_equal(to_info(dev_abcd)->reg_len, 0x500, "abcd len"); +} + +static void test_cs_gpios(void) +{ + zassert_equal(DT_SPI_HAS_CS(TEST_SPI_NO_CS), 0, "unexpected cs"); + zassert_equal(DT_SPI_NUM_CS(TEST_SPI_NO_CS), 0, "wrong no. of cs"); + + zassert_equal(DT_SPI_HAS_CS(TEST_SPI), 1, "missing cs"); + zassert_equal(DT_SPI_NUM_CS(TEST_SPI), 2, "wrong no. of cs"); + + zassert_true(!strcmp(DT_SPI_DEV_CS_GPIO_LABEL(TEST_SPI_DEV_0), + "TEST_GPIO_1"), + "dev 0 cs gpio name"); + zassert_equal(DT_SPI_DEV_CS_GPIO_PIN(TEST_SPI_DEV_0), 0x10, + "dev 0 cs gpio pin"); + zassert_equal(DT_SPI_DEV_CS_GPIO_FLAGS(TEST_SPI_DEV_0), 0x20, + "dev 0 cs gpio flags"); +} + +static void test_chosen(void) +{ + zassert_equal(DT_HAS_CHOSEN(ztest_xxxx), 0, "ztest_xxxx"); + zassert_equal(DT_HAS_CHOSEN(ztest_gpio), 1, "ztest_gpio"); + zassert_true(!strcmp(TO_STRING(DT_CHOSEN(ztest_gpio)), + "DT_N_S_test_S_gpio_deadbeef"), + "chosen"); +} + +static void test_enums(void) +{ + zassert_equal(DT_ENUM_IDX(DT_NODELABEL(test_enum_0), val), 0, "0"); +} + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_adc_temp_sensor +static void test_clocks(void) +{ + /* DT_CLOCKS_LABEL_BY_IDX */ + zassert_true(!strcmp(DT_CLOCKS_LABEL_BY_IDX(TEST_TEMP, 0), + "TEST_CLOCK"), + "label 0"); + + /* DT_CLOCKS_LABEL */ + zassert_true(!strcmp(DT_CLOCKS_LABEL(TEST_TEMP), "TEST_CLOCK"), + "label 0"); + + /* DT_CLOCKS_CELL_BY_IDX */ + zassert_equal(DT_CLOCKS_CELL_BY_IDX(TEST_TEMP, bits, 2), 2, + "clk 2 bits"); + zassert_equal(DT_CLOCKS_CELL_BY_IDX(TEST_TEMP, bus, 2), 8, "clk 2 bus"); + + /* DT_CLOCKS_CELL */ + zassert_equal(DT_CLOCKS_CELL(TEST_TEMP, bits), 7, "clk bits"); + zassert_equal(DT_CLOCKS_CELL(TEST_TEMP, bus), 3, "clk bus"); + + /* clock-freq on fixed clock */ + zassert_equal(DT_PROP_BY_PHANDLE_IDX(TEST_TEMP, clocks, 1, + clock_frequency), 25000000, + "fixed clk freq"); + + /* DT_INST */ + zassert_equal(DT_NUM_INST(DT_DRV_COMPAT), 1, "one instance"); + + /* DT_INST_CLOCKS_LABEL_BY_IDX */ + zassert_true(!strcmp(DT_INST_CLOCKS_LABEL_BY_IDX(0, 0), + "TEST_CLOCK"), + "label 0"); + + /* DT_INST_CLOCKS_LABEL */ + zassert_true(!strcmp(DT_INST_CLOCKS_LABEL(0), "TEST_CLOCK"), + "label 0"); + + /* DT_INST_CLOCKS_CELL_BY_IDX */ + zassert_equal(DT_INST_CLOCKS_CELL_BY_IDX(0, bits, 2), 2, + "clk 2 bits"); + zassert_equal(DT_INST_CLOCKS_CELL_BY_IDX(0, bus, 2), 8, "clk 2 bus"); + + /* DT_INST_CLOCKS_CELL */ + zassert_equal(DT_INST_CLOCKS_CELL(0, bits), 7, "clk bits"); + zassert_equal(DT_INST_CLOCKS_CELL(0, bus), 3, "clk bus"); + + /* clock-freq on fixed clock */ + zassert_equal(DT_INST_PROP_BY_PHANDLE_IDX(0, clocks, 1, + clock_frequency), 25000000, + "fixed clk freq"); +} + +void test_main(void) +{ + ztest_test_suite(devicetree_api, + ztest_unit_test(test_path_props), + ztest_unit_test(test_alias_props), + ztest_unit_test(test_nodelabel_props), + ztest_unit_test(test_inst_props), + ztest_unit_test(test_has_path), + ztest_unit_test(test_has_alias), + ztest_unit_test(test_has_inst), + ztest_unit_test(test_has_nodelabel), + ztest_unit_test(test_has_compat), + ztest_unit_test(test_bus), + ztest_unit_test(test_reg), + ztest_unit_test(test_irq), + ztest_unit_test(test_phandles), + ztest_unit_test(test_gpio), + ztest_unit_test(test_io_channels), + ztest_unit_test(test_macro_names), + ztest_unit_test(test_arrays), + ztest_unit_test(test_devices), + ztest_unit_test(test_cs_gpios), + ztest_unit_test(test_chosen), + ztest_unit_test(test_enums), + ztest_unit_test(test_clocks) + ); + ztest_run_test_suite(devicetree_api); +} diff --git a/tests/lib/devicetree/testcase.yaml b/tests/lib/devicetree/testcase.yaml new file mode 100644 index 00000000000..e558d819d18 --- /dev/null +++ b/tests/lib/devicetree/testcase.yaml @@ -0,0 +1,6 @@ +tests: + libraries.devicetree: + tags: devicetree + # We only need this to run on one platform so use native_posix as it + # will mostly likely be the fastest. + platform_whitelist: native_posix