text: /*
   * 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
  
  #include <devicetree_unfixed.h>
  #include <devicetree_fixups.h>
  
  #include <sys/util.h>
  
  /**
   * @brief devicetree.h API
   * @defgroup devicetree Devicetree
   * @{
   * @}
   */
  
  /*
   * Property suffixes
   * -----------------
   *
   * These are the optional parts that come after the _P_<property>
   * part in DT_N_<path-id>_P_<property-id> macros, or the "prop-suf"
   * nonterminal in the DT guide's macros.bnf file.
   *
   * Before adding new ones, check this list to avoid conflicts. If any
   * are missing from this list, please add them. It should be complete.
   *
   * _ENUM_IDX: property's value as an index into bindings enum
   * _ENUM_TOKEN: property's value as a token into bindings enum (string
   *              enum values are identifiers) [deprecated, use _STRING_TOKEN]
   * _ENUM_UPPER_TOKEN: like _ENUM_TOKEN, but uppercased [deprecated, use
   *		      _STRING_UPPER_TOKEN]
   * _EXISTS: property is defined
   * _FOREACH_PROP_ELEM: helper for "iterating" over values in the property
   * _FOREACH_PROP_ELEM_VARGS: foreach functions with variable number of arguments
   * _IDX_<i>: logical index into property
   * _IDX_<i>_EXISTS: logical index into property is defined
   * _IDX_<i>_PH: phandle array's phandle by index (or phandle, phandles)
   * _IDX_<i>_VAL_<val>: phandle array's specifier value by index
   * _IDX_<i>_VAL_<val>_EXISTS: cell value exists, by index
   * _LEN: property logical length
   * _NAME_<name>_PH: phandle array's phandle by name
   * _NAME_<name>_VAL_<val>: phandle array's property specifier by name
   * _NAME_<name>_VAL_<val>_EXISTS: cell value exists, by name
   * _STRING_TOKEN: string property's value as a token
   * _STRING_UPPER_TOKEN: like _STRING_TOKEN, but uppercased
   */
  
  /**
   * @defgroup devicetree-generic-id Node identifiers and helpers
   * @ingroup devicetree
   * @{
   */
  
  /**
   * @brief Name for an invalid node identifier
   *
   * This supports cases where factored macros can be invoked from paths where
   * devicetree data may or may not be available.  It is a preprocessor identifier
   * that does not match any valid devicetree node identifier.
   */
  #define DT_INVALID_NODE _
  
  /**
   * @brief Node identifier for the root node in the devicetree
   */
  #define DT_ROOT DT_N
  
  /**
   * @brief Get a node identifier for a devicetree path
   *
   * (This macro returns a node identifier from path components. To get
   * a path string from a node identifier, use DT_NODE_PATH() instead.)
   *
   * The arguments to this macro are the names of non-root nodes in the
   * tree required to reach the desired node, starting from the root.
   * Non-alphanumeric characters in each name must be converted to
   * underscores to form valid C tokens, and letters must be lowercased.
   *
   * Example devicetree fragment:
   *
   *     / {
   *             soc {
   *                     serial1: serial@40001000 {
   *                             status = "okay";
   *                             current-speed = <115200>;
   *                             ...
   *                     };
   *             };
   *     };
   *
   * You can use DT_PATH(soc, serial_40001000) to get a node identifier
   * for the serial@40001000 node. Node labels like "serial1" cannot be
   * used as DT_PATH() arguments; use DT_NODELABEL() for those instead.
   *
   * Example usage with DT_PROP() to get the current-speed property:
   *
   *     DT_PROP(DT_PATH(soc, serial_40001000), current_speed) // 115200
   *
   * (The current-speed property is also in "lowercase-and-underscores"
   * form when used with this API.)
   *
   * When determining arguments to DT_PATH():
   *
   * - the first argument corresponds to a child node of the root ("soc" above)
   * - a second argument corresponds to a child of the first argument
   *   ("serial_40001000" above, from the node name "serial@40001000"
   *   after lowercasing and changing "@" to "_")
   * - and so on for deeper nodes in the desired node's path
   *
   * @param ... lowercase-and-underscores node names along the node's path,
   *            with each name given as a separate argument
   * @return node identifier for the node with that path
   */
  #define DT_PATH(...) DT_PATH_INTERNAL(__VA_ARGS__)
  
  /**
   * @brief Get a node identifier for a node label
   *
   * Convert non-alphanumeric characters in the node label to
   * underscores to form valid C tokens, and lowercase all letters. Note
   * that node labels are not the same thing as label properties.
   *
   * Example devicetree fragment:
   *
   *     serial1: serial@40001000 {
   *             label = "UART_0";
   *             status = "okay";
   *             current-speed = <115200>;
   *             ...
   *     };
   *
   * The only node label in this example is "serial1".
   *
   * The string "UART_0" is *not* a node label; it's the value of a
   * property named label.
   *
   * You can use DT_NODELABEL(serial1) to get a node identifier for the
   * serial@40001000 node. Example usage with DT_PROP() to get the
   * current-speed property:
   *
   *     DT_PROP(DT_NODELABEL(serial1), current_speed) // 115200
   *
   * Another example devicetree fragment:
   *
   *     cpu@0 {
   *            L2_0: l2-cache {
   *                    cache-level = <2>;
   *                    ...
   *            };
   *     };
   *
   * Example usage to get the cache-level property:
   *
   *     DT_PROP(DT_NODELABEL(l2_0), cache_level) // 2
   *
   * Notice how "L2_0" in the devicetree is lowercased to "l2_0" in the
   * DT_NODELABEL() argument.
   *
   * @param label lowercase-and-underscores node label name
   * @return node identifier for the node with that label
   */
  #define DT_NODELABEL(label) DT_CAT(DT_N_NODELABEL_, label)
  
  /**
   * @brief Get a node identifier from /aliases
   *
   * This macro's argument is a property of the /aliases node. It
   * returns a node identifier for the node which is aliased. Convert
   * non-alphanumeric characters in the alias property to underscores to
   * form valid C tokens, and lowercase all letters.
   *
   * Example devicetree fragment:
   *
   *     / {
   *             aliases {
   *                     my-serial = &serial1;
   *             };
   *
   *             soc {
   *                     serial1: serial@40001000 {
   *                             status = "okay";
   *                             current-speed = <115200>;
   *                             ...
   *                     };
   *             };
   *     };
   *
   * You can use DT_ALIAS(my_serial) to get a node identifier for the
   * serial@40001000 node. Notice how my-serial in the devicetree
   * becomes my_serial in the DT_ALIAS() argument. Example usage with
   * DT_PROP() to get the current-speed property:
   *
   *     DT_PROP(DT_ALIAS(my_serial), current_speed) // 115200
   *
   * @param alias lowercase-and-underscores alias name.
   * @return node identifier for the node with that alias
   */
  #define DT_ALIAS(alias) DT_CAT(DT_N_ALIAS_, alias)
  
  /**
   * @brief Get a node identifier for an instance of a compatible
   *
   * All nodes with a particular compatible property value are assigned
   * instance numbers, which are zero-based indexes specific to that
   * compatible. You can get a node identifier for these nodes by
   * passing DT_INST() an instance number, "inst", along with the
   * lowercase-and-underscores version of the compatible, "compat".
   *
   * Instance numbers have the following properties:
   *
   * - for each compatible, instance numbers start at 0 and are contiguous
   * - exactly one instance number is assigned for each node with a compatible,
   *   **including disabled nodes**
   * - enabled nodes (status property is "okay" or missing) are assigned the
   *   instance numbers starting from 0, and disabled nodes have instance
   *   numbers which are greater than those of any enabled node
   *
   * No other guarantees are made. In particular:
   *
   * - instance numbers **in no way reflect** any numbering scheme that
   *   might exist in SoC documentation, node labels or unit addresses,
   *   or properties of the /aliases node (use DT_NODELABEL() or DT_ALIAS()
   *   for those)
   * - there **is no general guarantee** that the same node will have
   *   the same instance number between builds, even if you are building
   *   the same application again in the same build directory
   *
   * Example devicetree fragment:
   *
   *     serial1: serial@40001000 {
   *             compatible = "vnd,soc-serial";
   *             status = "disabled";
   *             current-speed = <9600>;
   *             ...
   *     };
   *
   *     serial2: serial@40002000 {
   *             compatible = "vnd,soc-serial";
   *             status = "okay";
   *             current-speed = <57600>;
   *             ...
   *     };
   *
   *     serial3: serial@40003000 {
   *             compatible = "vnd,soc-serial";
   *             current-speed = <115200>;
   *             ...
   *     };
   *
   * Assuming no other nodes in the devicetree have compatible
   * "vnd,soc-serial", that compatible has nodes with instance numbers
   * 0, 1, and 2.
   *
   * The nodes serial@40002000 and serial@40003000 are both enabled, so
   * their instance numbers are 0 and 1, but no guarantees are made
   * regarding which node has which instance number.
   *
   * Since serial@40001000 is the only disabled node, it has instance
   * number 2, since disabled nodes are assigned the largest instance
   * numbers. Therefore:
   *
   *     // Could be 57600 or 115200. There is no way to be sure:
   *     // either serial@40002000 or serial@40003000 could
   *     // have instance number 0, so this could be the current-speed
   *     // property of either of those nodes.
   *     DT_PROP(DT_INST(0, vnd_soc_serial), current_speed)
   *
   *     // Could be 57600 or 115200, for the same reason.
   *     // If the above expression expands to 57600, then
   *     // this expands to 115200, and vice-versa.
   *     DT_PROP(DT_INST(1, vnd_soc_serial), current_speed)
   *
   *     // 9600, because there is only one disabled node, and
   *     // disabled nodes are "at the the end" of the instance
   *     // number "list".
   *     DT_PROP(DT_INST(2, vnd_soc_serial), current_speed)
   *
   * Notice how "vnd,soc-serial" in the devicetree becomes vnd_soc_serial
   * (without quotes) in the DT_INST() arguments. (As usual, current-speed
   * in the devicetree becomes current_speed as well.)
   *
   * Nodes whose "compatible" property has multiple values are assigned
   * independent instance numbers for each compatible.
   *
   * @param inst instance number for compatible "compat"
   * @param compat lowercase-and-underscores compatible, without quotes
   * @return node identifier for the node with that instance number and
   *         compatible
   */
  #define DT_INST(inst, compat) UTIL_CAT(DT_N_INST, DT_DASH(inst, compat))
  
  /**
   * @brief Get a node identifier for a parent node
   *
   * Example devicetree fragment:
   *
   *     parent: parent-node {
   *             child: child-node {
   *                     ...
   *             };
   *     };
   *
   * The following are equivalent ways to get the same node identifier:
   *
   *     DT_NODELABEL(parent)
   *     DT_PARENT(DT_NODELABEL(child))
   *
   * @param node_id node identifier
   * @return a node identifier for the node's parent
   */
  #define DT_PARENT(node_id) UTIL_CAT(node_id, _PARENT)
  
  /**
   * @brief Get a node identifier for a grandparent node
   *
   * Example devicetree fragment:
   *
   *     gparent: grandparent-node {
   *             parent: parent-node {
   *                     child: child-node { ... }
   *             };
   *     };
   *
   * The following are equivalent ways to get the same node identifier:
   *
   *     DT_GPARENT(DT_NODELABEL(child))
   *     DT_PARENT(DT_PARENT(DT_NODELABEL(child))
   *
   * @param node_id node identifier
   * @return a node identifier for the node's parent's parent
   */
  #define DT_GPARENT(node_id) DT_PARENT(DT_PARENT(node_id))
  
  /**
   * @brief Get a node identifier for a child node
   *
   * Example devicetree fragment:
   *
   *     / {
   *             soc-label: soc {
   *                     serial1: serial@40001000 {
   *                             status = "okay";
   *                             current-speed = <115200>;
   *                             ...
   *                     };
   *             };
   *     };
   *
   * Example usage with @ref DT_PROP() to get the status of the
   * serial@40001000 node:
   *
   *     #define SOC_NODE DT_NODELABEL(soc_label)
   *     DT_PROP(DT_CHILD(SOC_NODE, serial_40001000), status) // "okay"
   *
   * Node labels like "serial1" cannot be used as the "child" argument
   * to this macro. Use DT_NODELABEL() for that instead.
   *
   * You can also use DT_FOREACH_CHILD() to iterate over node
   * identifiers for all of a node's children.
   *
   * @param node_id node identifier
   * @param child lowercase-and-underscores child node name
   * @return node identifier for the node with the name referred to by 'child'
   */
  #define DT_CHILD(node_id, child) UTIL_CAT(node_id, DT_S_PREFIX(child))
  
  /**
   * @brief Get a node identifier for a status "okay" node with a compatible
   *
   * Use this if you want to get an arbitrary enabled node with a given
   * compatible, and you do not care which one you get. If any enabled
   * nodes with the given compatible exist, a node identifier for one
   * of them is returned. Otherwise, @p DT_INVALID_NODE is returned.
   *
   * Example devicetree fragment:
   *
   *	node-a {
   *		compatible = "vnd,device";
   *		status = "okay";
   *	};
   *
   *	node-b {
   *		compatible = "vnd,device";
   *		status = "okay";
   *	};
   *
   *	node-c {
   *		compatible = "vnd,device";
   *		status = "disabled";
   *	};
   *
   * Example usage:
   *
   *     DT_COMPAT_GET_ANY_STATUS_OKAY(vnd_device)
   *
   * This expands to a node identifier for either @p node-a or @p
   * node-b. It will not expand to a node identifier for @p node-c,
   * because that node does not have status "okay".
   *
   * @param compat lowercase-and-underscores compatible, without quotes
   * @return node identifier for a node with that compatible, or DT_INVALID_NODE
   */
  #define DT_COMPAT_GET_ANY_STATUS_OKAY(compat)			\
  	COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(compat),	\
  		    (DT_INST(0, compat)),		\
  		    (DT_INVALID_NODE))
  
  /**
   * @brief Get a devicetree node's full path as a string literal
   *
   * This returns the path to a node from a node identifier. To get a
   * node identifier from path components instead, use DT_PATH().
   *
   * Example devicetree fragment:
   *
   *     / {
   *             soc {
   *                     node: my-node@12345678 { ... };
   *             };
   *     };
   *
   * Example usage:
   *
   *    DT_NODE_PATH(DT_NODELABEL(node)) // "/soc/my-node@12345678"
   *    DT_NODE_PATH(DT_PATH(soc))       // "/soc"
   *    DT_NODE_PATH(DT_ROOT)            // "/"
   *
   * @param node_id node identifier
   * @return the node's full path in the devicetree
   */
  #define DT_NODE_PATH(node_id) DT_CAT(node_id, _PATH)
  
  /**
   * @brief Get a devicetree node's name with unit-address as a string literal
   *
   * This returns the node name and unit-address from a node identifier.
   *
   * Example devicetree fragment:
   *
   *     / {
   *             soc {
   *                     node: my-node@12345678 { ... };
   *             };
   *     };
   *
   * Example usage:
   *
   *    DT_NODE_FULL_NAME(DT_NODELABEL(node)) // "my-node@12345678"
   *
   * @param node_id node identifier
   * @return the node's name with unit-address as a string in the devicetree
   */
  #define DT_NODE_FULL_NAME(node_id) DT_CAT(node_id, _FULL_NAME)
  
  /**
   * @brief Do node_id1 and node_id2 refer to the same node?
   *
   * Both "node_id1" and "node_id2" must be node identifiers for nodes
   * that exist in the devicetree (if unsure, you can check with
   * DT_NODE_EXISTS()).
   *
   * The expansion evaluates to 0 or 1, but may not be a literal integer
   * 0 or 1.
   *
   * @param node_id1 first node identifer
   * @param node_id2 second node identifier
   * @return an expression that evaluates to 1 if the node identifiers
   *         refer to the same node, and evaluates to 0 otherwise
   */
  #define DT_SAME_NODE(node_id1, node_id2) \
  	(DT_DEP_ORD(node_id1) == (DT_DEP_ORD(node_id2)))
  
  /* Implementation note: distinct nodes have distinct node identifiers.
   * See include/devicetree/ordinals.h. */
  
  /**
   * @}
   */
  
  /**
   * @defgroup devicetree-generic-prop Property accessors
   * @ingroup devicetree
   * @{
   */
  
  /**
   * @brief Get a devicetree property value
   *
   * For properties whose bindings have the following types, this macro
   * expands to:
   *
   * - string: a string literal
   * - boolean: 0 if the property is false, or 1 if it is true
   * - int: the property's value as an integer literal
   * - array, uint8-array, string-array: an initializer expression in braces,
   *   whose elements are integer or string literals (like {0, 1, 2},
   *   {"hello", "world"}, etc.)
   * - phandle: a node identifier for the node with that phandle
   *
   * A property's type is usually defined by its binding. In some
   * special cases, it has an assumed type defined by the devicetree
   * specification even when no binding is available: "compatible" has
   * type string-array, "status" and "label" have type string, and
   * "interrupt-controller" has type boolean.
   *
   * For other properties or properties with unknown type due to a
   * missing binding, behavior is undefined.
   *
   * For usage examples, see @ref DT_PATH(), @ref DT_ALIAS(), @ref
   * DT_NODELABEL(), and @ref DT_INST() above.
   *
   * @param node_id node identifier
   * @param prop lowercase-and-underscores property name
   * @return a representation of the property's value
   */
  #define DT_PROP(node_id, prop) DT_CAT(node_id, _P_##prop)
  
  /**
   * @brief Get a property's logical length
   *
   * Here, "length" is a number of elements, which may differ from the
   * property's size in bytes.
   *
   * The return value depends on the property's type:
   *
   * - for types array, string-array, and uint8-array, this expands
   *   to the number of elements in the array
   * - for type phandles, this expands to the number of phandles
   * - for type phandle-array, this expands to the number of
   *   phandle and specifier blocks in the property
   *
   * These properties are handled as special cases:
   *
   * - reg property: use DT_NUM_REGS(node_id) instead
   * - interrupts property: use DT_NUM_IRQS(node_id) instead
   *
   * It is an error to use this macro with the reg or interrupts properties.
   *
   * For other properties, behavior is undefined.
   *
   * @param node_id node identifier
   * @param prop a lowercase-and-underscores property with a logical length
   * @return the property's length
   */
  #define DT_PROP_LEN(node_id, prop) DT_PROP(node_id, prop##_LEN)
  
  /**
   * @brief Like DT_PROP_LEN(), but with a fallback to default_value
   *
   * If the property is defined (as determined by DT_NODE_HAS_PROP()),
   * this expands to DT_PROP_LEN(node_id, prop). The default_value
   * parameter is not expanded in this case.
   *
   * Otherwise, this expands to default_value.
   *
   * @param node_id node identifier
   * @param prop a lowercase-and-underscores property with a logical length
   * @param default_value a fallback value to expand to
   * @return the property's length or the given default value
   */
  #define DT_PROP_LEN_OR(node_id, prop, default_value) \
  	COND_CODE_1(DT_NODE_HAS_PROP(node_id, prop), \
  		    (DT_PROP_LEN(node_id, prop)), (default_value))
  
  /**
   * @brief Is index "idx" valid for an array type property?
   *
   * If this returns 1, then DT_PROP_BY_IDX(node_id, prop, idx) or
   * DT_PHA_BY_IDX(node_id, prop, idx, ...) are valid at index "idx".
   * If it returns 0, it is an error to use those macros with that index.
   *
   * These properties are handled as special cases:
   *
   * - reg property: use DT_REG_HAS_IDX(node_id, idx) instead
   * - interrupts property: use DT_IRQ_HAS_IDX(node_id, idx) instead
   *
   * It is an error to use this macro with the reg or interrupts properties.
   *
   * @param node_id node identifier
   * @param prop a lowercase-and-underscores property with a logical length
   * @param idx index to check
   * @return An expression which evaluates to 1 if "idx" is a valid index
   *         into the given property, and 0 otherwise.
   */
  #define DT_PROP_HAS_IDX(node_id, prop, idx) \
  	IS_ENABLED(DT_CAT6(node_id, _P_, prop, _IDX_, idx, _EXISTS))
  
  /**
   * @brief Get the value at index "idx" in an array type property
   *
   * It might help to read the argument order as being similar to
   * "node->property[index]".
   *
   * When the property's binding has type array, string-array,
   * uint8-array, or phandles, this expands to the idx-th array element
   * as an integer, string literal, or node identifier respectively.
   *
   * These properties are handled as special cases:
   *
   * - reg property: use DT_REG_ADDR_BY_IDX() or DT_REG_SIZE_BY_IDX() instead
   * - interrupts property: use DT_IRQ_BY_IDX() instead
   *
   * For non-array properties, behavior is undefined.
   *
   * @param node_id node identifier
   * @param prop lowercase-and-underscores property name
   * @param idx the index to get
   * @return a representation of the idx-th element of the property
   */
  #define DT_PROP_BY_IDX(node_id, prop, idx) DT_PROP(node_id, prop##_IDX_##idx)
  
  /**
   * @brief Like DT_PROP(), but with a fallback to default_value
   *
   * If the value exists, this expands to DT_PROP(node_id, prop).
   * The default_value parameter is not expanded in this case.
   *
   * Otherwise, this expands to default_value.
   *
   * @param node_id node identifier
   * @param prop lowercase-and-underscores property name
   * @param default_value a fallback value to expand to
   * @return the property's value or default_value
   */
  #define DT_PROP_OR(node_id, prop, default_value) \
  	COND_CODE_1(DT_NODE_HAS_PROP(node_id, prop), \
  		    (DT_PROP(node_id, prop)), (default_value))
  
  /**
   * @brief Equivalent to DT_PROP(node_id, label)
   *
   * This is a convenience for the Zephyr device API, which uses label
   * properties as device_get_binding() arguments.
   * @param node_id node identifier
   * @return node's label property value
   */
  #define DT_LABEL(node_id) DT_PROP(node_id, label)
  
  /**
   * @brief Get a property value's index into its enumeration values
   *
   * The return values start at zero.
   *
   * Example devicetree fragment:
   *
   *     usb1: usb@12340000 {
   *             maximum-speed = "full-speed";
   *     };
   *     usb2: usb@12341000 {
   *             maximum-speed = "super-speed";
   *     };
   *
   * Example bindings fragment:
   *
   *     properties:
   *       maximum-speed:
   *         type: string
   *         enum:
   *            - "low-speed"
   *            - "full-speed"
   *            - "high-speed"
   *            - "super-speed"
   *
   * Example usage:
   *
   *     DT_ENUM_IDX(DT_NODELABEL(usb1), maximum_speed) // 1
   *     DT_ENUM_IDX(DT_NODELABEL(usb2), maximum_speed) // 3
   *
   * @param node_id node identifier
   * @param prop lowercase-and-underscores property name
   * @return zero-based index of the property's value in its enum: list
   */
  #define DT_ENUM_IDX(node_id, prop) DT_PROP(node_id, prop##_ENUM_IDX)
  
  /**
   * @brief Like DT_ENUM_IDX(), but with a fallback to a default enum index
   *
   * If the value exists, this expands to its zero based index value thanks to
   * DT_ENUM_IDX(node_id, prop).
   *
   * Otherwise, this expands to provided default index enum value.
   *
   * @param node_id node identifier
   * @param prop lowercase-and-underscores property name
   * @param default_idx_value a fallback index value to expand to
   * @return zero-based index of the property's value in its enum if present,
   *         default_idx_value ohterwise
   */
  #define DT_ENUM_IDX_OR(node_id, prop, default_idx_value) \
  	COND_CODE_1(DT_NODE_HAS_PROP(node_id, prop), \
  		    (DT_ENUM_IDX(node_id, prop)), (default_idx_value))
  
  /**
   * @brief Get a string property's value as a token.
   *
   * This removes "the quotes" from string-valued properties, and converts
   * non-alphanumeric characters to underscores. That can be useful, for example,
   * when programmatically using the value to form a C variable or code.
   *
   * DT_STRING_TOKEN() can only be used for properties with string type.
   *
   * It is an error to use DT_STRING_TOKEN() in other circumstances.
   *
   * Example devicetree fragment:
   *
   *     n1: node-1 {
   *             prop = "foo";
   *     };
   *     n2: node-2 {
   *             prop = "FOO";
   *     }
   *     n3: node-3 {
   *             prop = "123 foo";
   *     };
   *
   * Example bindings fragment:
   *
   *     properties:
   *       prop:
   *         type: string
   *
   * Example usage:
   *
   *     DT_STRING_TOKEN(DT_NODELABEL(n1), prop) // foo
   *     DT_STRING_TOKEN(DT_NODELABEL(n2), prop) // FOO
   *     DT_STRING_TOKEN(DT_NODELABEL(n3), prop) // 123_foo
   *
   * Notice how:
   *
   * - Unlike C identifiers, the property values may begin with a
   *   number. It's the user's responsibility not to use such values as
   *   the name of a C identifier.
   *
   * - The uppercased "FOO" in the DTS remains @p FOO as a token. It is
   *   *not* converted to @p foo.
   *
   * - The whitespace in the DTS "123 foo" string is converted to @p
   *   123_foo as a token.
   *
   * @param node_id node identifier
   * @param prop lowercase-and-underscores property string name
   * @return the value of @p prop as a token, i.e. without any quotes
   *         and with special characters converted to underscores
   */
  #define DT_STRING_TOKEN(node_id, prop) \
  	DT_CAT4(node_id, _P_, prop, _STRING_TOKEN)
  
  /**
   * @brief Like DT_STRING_TOKEN(), but uppercased.
   *
   * This removes "the quotes and capitalize" from string-valued properties, and
   * converts non-alphanumeric characters to underscores. That can be useful, for
   * example, when programmatically using the value to form a C variable or code.
   *
   * DT_STRING_UPPER_TOKEN() can only be used for properties with string type.
   *
   * It is an error to use DT_STRING_UPPER_TOKEN() in other circumstances.
   *
   * Example devicetree fragment:
   *
   *     n1: node-1 {
   *             prop = "foo";
   *     };
   *     n2: node-2 {
   *             prop = "123 foo";
   *     };
   *
   * Example bindings fragment:
   *
   *     properties:
   *       prop:
   *         type: string
   *
   * Example usage:
   *
   *     DT_STRING_UPPER_TOKEN(DT_NODELABEL(n1), prop) // FOO
   *     DT_STRING_UPPER_TOKEN(DT_NODELABEL(n2), prop) // 123_FOO
   *
   * Notice how:
   *
   * - Unlike C identifiers, the property values may begin with a
   *   number. It's the user's responsibility not to use such values as
   *   the name of a C identifier.
   *
   * - The lowercased "foo" in the DTS becomes @p FOO as a token, i.e.
   *   it is uppercased.
   *
   * - The whitespace in the DTS "123 foo" string is converted to @p
   *   123_FOO as a token, i.e. it is uppercased and whitespace becomes
   *   an underscore.
   *
   * @param node_id node identifier
   * @param prop lowercase-and-underscores property string name
   * @return the value of @p prop as a token, i.e. without any quotes
   *         and with special characters converted to underscores
   */
  #define DT_STRING_UPPER_TOKEN(node_id, prop) \
  	DT_CAT4(node_id, _P_, prop, _STRING_UPPER_TOKEN)
  
  /**
   * @brief Get an enumeration property's value as a token.
   *
   * This allows you to "remove the quotes" from some string-valued
   * properties. That can be useful, for example, when pasting the
   * values onto some other token to form an enum in C using the @p ##
   * preprocessor operator.
   *
   * DT_ENUM_TOKEN() can only be used for properties with string type
   * whose binding has an "enum:". The values in the binding's "enum:"
   * list must be unique after converting non-alphanumeric characters to
   * underscores.
   *
   * It is an error to use DT_ENUM_TOKEN() in other circumstances.
   *
   * Example devicetree fragment:
   *
   *     n1: node-1 {
   *             prop = "foo";
   *     };
   *     n2: node-2 {
   *             prop = "FOO";
   *     }
   *     n3: node-3 {
   *             prop = "123 foo";
   *     };
   *
   * Example bindings fragment:
   *
   *     properties:
   *       prop:
   *         type: string
   *         enum:
   *            - "foo"
   *            - "FOO"
   *            - "123 foo"
   *
   * Example usage:
   *
   *     DT_ENUM_TOKEN(DT_NODELABEL(n1), prop) // foo
   *     DT_ENUM_TOKEN(DT_NODELABEL(n2), prop) // FOO
   *     DT_ENUM_TOKEN(DT_NODELABEL(n3), prop) // 123_foo
   *
   * Notice how:
   *
   * - Unlike C identifiers, the property values may begin with a
   *   number. It's the user's responsibility not to use such values as
   *   the name of a C identifier.
   *
   * - The uppercased "FOO" in the DTS remains @p FOO as a token. It is
       *not* converted to @p foo.
   *
   * - The whitespace in the DTS "123 foo" string is converted to @p
   *   123_foo as a token.
   *
   * @param node_id node identifier
   * @param prop lowercase-and-underscores property name with suitable
   *             enumeration of values in its binding
   * @return the value of @p prop as a token, i.e. without any quotes
   *         and with special characters converted to underscores
   */
  #define DT_ENUM_TOKEN(node_id, prop) \
  	__DEPRECATED_MACRO \
  	DT_CAT4(node_id, _P_, prop, _ENUM_TOKEN)
  
  /**
   * @brief Like DT_ENUM_TOKEN(), but uppercased
   *
   * This allows you to "remove the quotes and capitalize" some string-valued
   * properties.
   *
   * DT_ENUM_UPPER_TOKEN() can only be used for properties with string type
   * whose binding has an "enum:". The values in the binding's "enum:"
   * list must be unique after converting non-alphanumeric characters to
   * underscores and capitalizating any letters.
   *
   * It is an error to use DT_ENUM_UPPER_TOKEN() in other circumstances.
   *
   * Example devicetree fragment:
   *
   *     n1: node-1 {
   *             prop = "foo";
   *     };
   *     n2: node-2 {
   *             prop = "123 foo";
   *     };
   *
   * Example bindings fragment:
   *
   *     properties:
   *       prop:
   *         type: string
   *         enum:
   *            - "foo"
   *            - "123 foo"
   *
   * Example usage:
   *
   *     DT_ENUM_TOKEN((DT_NODELABEL(n1), prop) // FOO
   *     DT_ENUM_TOKEN((DT_NODELABEL(n2), prop) // 123_FOO
   *
   * Notice how:
   *
   * - Unlike C identifiers, the property values may begin with a
   *   number. It's the user's responsibility not to use such values as
   *   the name of a C identifier.
   *
   * - The lowercased "foo" in the DTS becomes @p FOO as a token, i.e.
   *   it is uppercased.
   *
   * - The whitespace in the DTS "123 foo" string is converted to @p
   *   123_FOO as a token, i.e. it is uppercased and whitespace becomes
   *   an underscore.
   *
   * @param node_id node identifier
   * @param prop lowercase-and-underscores property name with suitable
   *             enumeration of values in its binding
   * @return the value of @p prop as a capitalized token, i.e. upper case,
   *         without any quotes, and with special characters converted to
   *         underscores
   */
  #define DT_ENUM_UPPER_TOKEN(node_id, prop) \
  	__DEPRECATED_MACRO \
  	DT_CAT4(node_id, _P_, prop, _ENUM_UPPER_TOKEN)
  
  /*
   * phandle properties
   *
   * These are special-cased to manage the impedance mismatch between
   * phandles, which are just uint32_t node properties that only make sense
   * within the tree itself, and C values.
   */
  
  /**
   * @brief Get a property value from a phandle in a property.
   *
   * This is a shorthand for:
   *
   *     DT_PROP(DT_PHANDLE_BY_IDX(node_id, phs, idx), prop)
   *
   * That is, "prop" is a property of the phandle's node, not a
   * property of "node_id".
   *
   * Example devicetree fragment:
   *
   *     n1: node-1 {
   *             foo = <&n2 &n3>;
   *     };
   *
   *     n2: node-2 {
   *             bar = <42>;
   *     };
   *
   *     n3: node-3 {
   *             baz = <43>;
   *     };
   *
   * Example usage:
   *
   *     #define N1 DT_NODELABEL(n1)
   *
   *     DT_PROP_BY_PHANDLE_IDX(N1, foo, 0, bar) // 42
   *     DT_PROP_BY_PHANDLE_IDX(N1, foo, 1, baz) // 43
   *
   * @param node_id node identifier
   * @param phs lowercase-and-underscores property with type "phandle",
   *            "phandles", or "phandle-array"
   * @param idx logical index into "phs", which must be zero if "phs"
   *            has type "phandle"
   * @param prop lowercase-and-underscores property of the phandle's node
   * @return the property's value
   */
  #define DT_PROP_BY_PHANDLE_IDX(node_id, phs, idx, prop) \
  	DT_PROP(DT_PHANDLE_BY_IDX(node_id, phs, idx), prop)
  
  /**
   * @brief Like DT_PROP_BY_PHANDLE_IDX(), but with a fallback to
   * default_value.
   *
   * If the value exists, this expands to DT_PROP_BY_PHANDLE_IDX(node_id, phs,
   * idx, prop). The default_value parameter is not expanded in this
   * case.
   *
   * Otherwise, this expands to default_value.
   *
   * @param node_id node identifier
   * @param phs lowercase-and-underscores property with type "phandle",
   *            "phandles", or "phandle-array"
   * @param idx logical index into "phs", which must be zero if "phs"
   *            has type "phandle"
   * @param prop lowercase-and-underscores property of the phandle's node
   * @param default_value a fallback value to expand to
   * @return the property's value
   */
  #define DT_PROP_BY_PHANDLE_IDX_OR(node_id, phs, idx, prop, default_value) \
  	DT_PROP_OR(DT_PHANDLE_BY_IDX(node_id, phs, idx), prop, default_value)
  
  /**
   * @brief Get a property value from a phandle's node
   *
   * This is equivalent to DT_PROP_BY_PHANDLE_IDX(node_id, ph, 0, prop).
   *
   * @param node_id node identifier
   * @param ph lowercase-and-underscores property of "node_id"
   *           with type "phandle"
   * @param prop lowercase-and-underscores property of the phandle's node
   * @return the property's value
   */
  #define DT_PROP_BY_PHANDLE(node_id, ph, prop) \
  	DT_PROP_BY_PHANDLE_IDX(node_id, ph, 0, prop)
  
  /**
   * @brief Get a phandle-array specifier cell value at an index
   *
   * It might help to read the argument order as being similar to
   * "node->phandle_array[index].cell". That is, the cell value is in
   * the "pha" property of "node_id", inside the specifier at index
   * "idx".
   *
   * Example devicetree fragment:
   *
   *     gpio0: gpio@... {
   *             #gpio-cells = <2>;
   *     };
   *
   *     gpio1: gpio@... {
   *             #gpio-cells = <2>;
   *     };
   *
   *     led: led_0 {
   *             gpios = <&gpio0 17 0x1>, <&gpio1 5 0x3>;
   *     };
   *
   * Bindings fragment for the gpio0 and gpio1 nodes:
   *
   *     gpio-cells:
   *       - pin
   *       - flags
   *
   * Above, "gpios" has two elements:
   *
   * - index 0 has specifier <17 0x1>, so its "pin" cell is 17, and its
   *   "flags" cell is 0x1
   * - index 1 has specifier <5 0x3>, so "pin" is 5 and "flags" is 0x3
   *
   * Example usage:
   *
   *     #define LED DT_NODELABEL(led)
   *
   *     DT_PHA_BY_IDX(LED, gpios, 0, pin)   // 17
   *     DT_PHA_BY_IDX(LED, gpios, 1, flags) // 0x3
   *
   * @param node_id node identifier
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param idx logical index into "pha"
   * @param cell lowercase-and-underscores cell name within the specifier
   *             at "pha" index "idx"
   * @return the cell's value
   */
  #define DT_PHA_BY_IDX(node_id, pha, idx, cell) \
  	DT_PROP(node_id, pha##_IDX_##idx##_VAL_##cell)
  
  /**
   * @brief Like DT_PHA_BY_IDX(), but with a fallback to default_value.
   *
   * If the value exists, this expands to DT_PHA_BY_IDX(node_id, pha,
   * idx, cell). The default_value parameter is not expanded in this
   * case.
   *
   * Otherwise, this expands to default_value.
   *
   * @param node_id node identifier
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param idx logical index into "pha"
   * @param cell lowercase-and-underscores cell name within the specifier
   *             at "pha" index "idx"
   * @param default_value a fallback value to expand to
   * @return the cell's value or "default_value"
   */
  #define DT_PHA_BY_IDX_OR(node_id, pha, idx, cell, default_value) \
  	DT_PROP_OR(node_id, pha##_IDX_##idx##_VAL_##cell, default_value)
  /* Implementation note: the _IDX_##idx##_VAL_##cell##_EXISTS
   * macros are defined, so it's safe to use DT_PROP_OR() here, because
   * that uses an IS_ENABLED() on the _EXISTS macro.
   */
  
  /**
   * @brief Equivalent to DT_PHA_BY_IDX(node_id, pha, 0, cell)
   * @param node_id node identifier
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param cell lowercase-and-underscores cell name
   * @return the cell's value
   */
  #define DT_PHA(node_id, pha, cell) DT_PHA_BY_IDX(node_id, pha, 0, cell)
  
  /**
   * @brief Like DT_PHA(), but with a fallback to default_value
   *
   * If the value exists, this expands to DT_PHA(node_id, pha, cell).
   * The default_value parameter is not expanded in this case.
   *
   * Otherwise, this expands to default_value.
   *
   * @param node_id node identifier
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param cell lowercase-and-underscores cell name
   * @param default_value a fallback value to expand to
   * @return the cell's value or default_value
   */
  #define DT_PHA_OR(node_id, pha, cell, default_value) \
  	DT_PHA_BY_IDX_OR(node_id, pha, 0, cell, default_value)
  
  /**
   * @brief Get a value within a phandle-array specifier by name
   *
   * This is like DT_PHA_BY_IDX(), except it treats "pha" as a structure
   * where each array element has a name.
   *
   * It might help to read the argument order as being similar to
   * "node->phandle_struct.name.cell". That is, the cell value is in the
   * "pha" property of "node_id", treated as a data structure where
   * each array element has a name.
   *
   * Example devicetree fragment:
   *
   *     n: node {
   *             io-channels = <&adc1 10>, <&adc2 20>;
   *             io-channel-names = "SENSOR", "BANDGAP";
   *     };
   *
   * Bindings fragment for the "adc1" and "adc2" nodes:
   *
   *     io-channel-cells:
   *       - input
   *
   * Example usage:
   *
   *     DT_PHA_BY_NAME(DT_NODELABEL(n), io_channels, sensor, input)  // 10
   *     DT_PHA_BY_NAME(DT_NODELABEL(n), io_channels, bandgap, input) // 20
   *
   * @param node_id node identifier
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param name lowercase-and-underscores name of a specifier in "pha"
   * @param cell lowercase-and-underscores cell name in the named specifier
   * @return the cell's value
   */
  #define DT_PHA_BY_NAME(node_id, pha, name, cell) \
  	DT_PROP(node_id, pha##_NAME_##name##_VAL_##cell)
  
  /**
   * @brief Like DT_PHA_BY_NAME(), but with a fallback to default_value
   *
   * If the value exists, this expands to DT_PHA_BY_NAME(node_id, pha,
   * name, cell). The default_value parameter is not expanded in this case.
   *
   * Otherwise, this expands to default_value.
   *
   * @param node_id node identifier
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param name lowercase-and-underscores name of a specifier in "pha"
   * @param cell lowercase-and-underscores cell name in the named specifier
   * @param default_value a fallback value to expand to
   * @return the cell's value or default_value
   */
  #define DT_PHA_BY_NAME_OR(node_id, pha, name, cell, default_value) \
  	DT_PROP_OR(node_id, pha##_NAME_##name##_VAL_##cell, default_value)
  /* Implementation note: the _NAME_##name##_VAL_##cell##_EXISTS
   * macros are defined, so it's safe to use DT_PROP_OR() here, because
   * that uses an IS_ENABLED() on the _EXISTS macro.
   */
  
  /**
   * @brief Get a phandle's node identifier from a phandle array by name
   *
   * It might help to read the argument order as being similar to
   * "node->phandle_struct.name.phandle". That is, the phandle array is
   * treated as a structure with named elements. The return value is
   * the node identifier for a phandle inside the structure.
   *
   * Example devicetree fragment:
   *
   *     adc1: adc@... {
   *             label = "ADC_1";
   *     };
   *
   *     adc2: adc@... {
   *             label = "ADC_2";
   *     };
   *
   *     n: node {
   *             io-channels = <&adc1 10>, <&adc2 20>;
   *             io-channel-names = "SENSOR", "BANDGAP";
   *     };
   *
   * Above, "io-channels" has two elements:
   *
   * - the element named "SENSOR" has phandle &adc1
   * - the element named "BANDGAP" has phandle &adc2
   *
   * Example usage:
   *
   *     #define NODE DT_NODELABEL(n)
   *
   *     DT_LABEL(DT_PHANDLE_BY_NAME(NODE, io_channels, sensor))  // "ADC_1"
   *     DT_LABEL(DT_PHANDLE_BY_NAME(NODE, io_channels, bandgap)) // "ADC_2"
   *
   * Notice how devicetree properties and names are lowercased, and
   * non-alphanumeric characters are converted to underscores.
   *
   * @param node_id node identifier
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param name lowercase-and-underscores name of an element in "pha"
   * @return a node identifier for the node with that phandle
   */
  #define DT_PHANDLE_BY_NAME(node_id, pha, name) \
  	DT_PROP(node_id, pha##_NAME_##name##_PH)
  
  /**
   * @brief Get a node identifier for a phandle in a property.
   *
   * When a node's value at a logical index contains a phandle, this
   * macro returns a node identifier for the node with that phandle.
   *
   * Therefore, if "prop" has type "phandle", "idx" must be zero. (A
   * "phandle" type is treated as a "phandles" with a fixed length of
   * 1).
   *
   * Example devicetree fragment:
   *
   *     n1: node-1 {
   *             foo = <&n2 &n3>;
   *     };
   *
   *     n2: node-2 { ... };
   *     n3: node-3 { ... };
   *
   * Above, "foo" has type phandles and has two elements:
   *
   * - index 0 has phandle &n2, which is node-2's phandle
   * - index 1 has phandle &n3, which is node-3's phandle
   *
   * Example usage:
   *
   *     #define N1 DT_NODELABEL(n1)
   *
   *     DT_PHANDLE_BY_IDX(N1, foo, 0) // node identifier for node-2
   *     DT_PHANDLE_BY_IDX(N1, foo, 1) // node identifier for node-3
   *
   * Behavior is analogous for phandle-arrays.
   *
   * @param node_id node identifier
   * @param prop lowercase-and-underscores property name in "node_id"
   *             with type "phandle", "phandles" or "phandle-array"
   * @param idx index into "prop"
   * @return node identifier for the node with the phandle at that index
   */
  #define DT_PHANDLE_BY_IDX(node_id, prop, idx) \
  	DT_CAT6(node_id, _P_, prop, _IDX_, idx, _PH)
  /*
   * Implementation note: using DT_CAT6 above defers concatenation until
   * after expansion of each parameter. This is important when 'idx' is
   * expandable to a number, but it isn't one "yet".
   */
  
  /**
   * @brief Get a node identifier for a phandle property's value
   *
   * This is equivalent to DT_PHANDLE_BY_IDX(node_id, prop, 0). Its primary
   * benefit is readability when "prop" has type "phandle".
   *
   * @param node_id node identifier
   * @param prop lowercase-and-underscores property of "node_id"
   *             with type "phandle"
   * @return a node identifier for the node pointed to by "ph"
   */
  #define DT_PHANDLE(node_id, prop) DT_PHANDLE_BY_IDX(node_id, prop, 0)
  
  /**
   * @}
   */
  
  /**
   * @defgroup devicetree-reg-prop reg property
   * @ingroup devicetree
   * @{
   */
  
  /**
   * @brief Get the number of register blocks in the reg property
   *
   * Use this instead of DT_PROP_LEN(node_id, reg).
   * @param node_id node identifier
   * @return Number of register blocks in the node's "reg" property.
   */
  #define DT_NUM_REGS(node_id) DT_CAT(node_id, _REG_NUM)
  
  /**
   * @brief Is "idx" a valid register block index?
   *
   * If this returns 1, then DT_REG_ADDR_BY_IDX(node_id, idx) or
   * DT_REG_SIZE_BY_IDX(node_id, idx) are valid.
   * If it returns 0, it is an error to use those macros with index "idx".
   * @param node_id node identifier
   * @param idx index to check
   * @return 1 if "idx" is a valid register block index,
   *         0 otherwise.
   */
  #define DT_REG_HAS_IDX(node_id, idx) \
  	IS_ENABLED(DT_CAT(node_id, _REG_IDX_##idx##_EXISTS))
  
  /**
   * @brief Get the base address of the register block at index "idx"
   * @param node_id node identifier
   * @param idx index of the register whose address to return
   * @return address of the idx-th register block
   */
  #define DT_REG_ADDR_BY_IDX(node_id, idx) \
  	DT_CAT(node_id, _REG_IDX_##idx##_VAL_ADDRESS)
  
  /**
   * @brief Get the size of the register block at index "idx"
   *
   * This is the size of an individual register block, not the total
   * number of register blocks in the property; use DT_NUM_REGS() for
   * that.
   *
   * @param node_id node identifier
   * @param idx index of the register whose size to return
   * @return size of the idx-th register block
   */
  #define DT_REG_SIZE_BY_IDX(node_id, idx) \
  	DT_CAT(node_id, _REG_IDX_##idx##_VAL_SIZE)
  
  /**
   * @brief Get a node's (only) register block address
   *
   * Equivalent to DT_REG_ADDR_BY_IDX(node_id, 0).
   * @param node_id node identifier
   * @return node's register block address
   */
  #define DT_REG_ADDR(node_id) DT_REG_ADDR_BY_IDX(node_id, 0)
  
  /**
   * @brief Get a node's (only) register block size
   *
   * Equivalent to DT_REG_SIZE_BY_IDX(node_id, 0).
   * @param node_id node identifier
   * @return node's only register block's size
   */
  #define DT_REG_SIZE(node_id) DT_REG_SIZE_BY_IDX(node_id, 0)
  
  /**
   * @brief Get a register block's base address by name
   * @param node_id node identifier
   * @param name lowercase-and-underscores register specifier name
   * @return address of the register block specified by name
   */
  #define DT_REG_ADDR_BY_NAME(node_id, name) \
  	DT_CAT(node_id, _REG_NAME_##name##_VAL_ADDRESS)
  
  /**
   * @brief Get a register block's size by name
   * @param node_id node identifier
   * @param name lowercase-and-underscores register specifier name
   * @return size of the register block specified by name
   */
  #define DT_REG_SIZE_BY_NAME(node_id, name) \
  	DT_CAT(node_id, _REG_NAME_##name##_VAL_SIZE)
  
  /**
   * @}
   */
  
  /**
   * @defgroup devicetree-interrupts-prop interrupts property
   * @ingroup devicetree
   * @{
   */
  
  /**
   * @brief Get the number of interrupt sources for the node
   *
   * Use this instead of DT_PROP_LEN(node_id, interrupts).
   *
   * @param node_id node identifier
   * @return Number of interrupt specifiers in the node's "interrupts" property.
   */
  #define DT_NUM_IRQS(node_id) DT_CAT(node_id, _IRQ_NUM)
  
  /**
   * @brief Is "idx" a valid interrupt index?
   *
   * If this returns 1, then DT_IRQ_BY_IDX(node_id, idx) is valid.
   * If it returns 0, it is an error to use that macro with this index.
   * @param node_id node identifier
   * @param idx index to check
   * @return 1 if the idx is valid for the interrupt property
   *         0 otherwise.
   */
  #define DT_IRQ_HAS_IDX(node_id, idx) \
  	IS_ENABLED(DT_CAT(node_id, _IRQ_IDX_##idx##_EXISTS))
  
  /**
   * @brief Does an interrupts property have a named cell specifier at an index?
   * If this returns 1, then DT_IRQ_BY_IDX(node_id, idx, cell) is valid.
   * If it returns 0, it is an error to use that macro.
   * @param node_id node identifier
   * @param idx index to check
   * @param cell named cell value whose existence to check
   * @return 1 if the named cell exists in the interrupt specifier at index idx
   *         0 otherwise.
   */
  #define DT_IRQ_HAS_CELL_AT_IDX(node_id, idx, cell) \
  	IS_ENABLED(DT_CAT(node_id, _IRQ_IDX_##idx##_VAL_##cell##_EXISTS))
  
  /**
   * @brief Equivalent to DT_IRQ_HAS_CELL_AT_IDX(node_id, 0, cell)
   * @param node_id node identifier
   * @param cell named cell value whose existence to check
   * @return 1 if the named cell exists in the interrupt specifier at index 0
   *         0 otherwise.
   */
  #define DT_IRQ_HAS_CELL(node_id, cell) DT_IRQ_HAS_CELL_AT_IDX(node_id, 0, cell)
  
  /**
   * @brief Does an interrupts property have a named specifier value at an index?
   * If this returns 1, then DT_IRQ_BY_NAME(node_id, name, cell) is valid.
   * If it returns 0, it is an error to use that macro.
   * @param node_id node identifier
   * @param name lowercase-and-underscores interrupt specifier name
   * @return 1 if "name" is a valid named specifier
   *         0 otherwise.
   */
  #define DT_IRQ_HAS_NAME(node_id, name) \
  	IS_ENABLED(DT_CAT(node_id, _IRQ_NAME_##name##_VAL_irq_EXISTS))
  
  /**
   * @brief Get a value within an interrupt specifier at an index
   *
   * It might help to read the argument order as being similar to
   * "node->interrupts[index].cell".
   *
   * This can be used to get information about an individual interrupt
   * when a device generates more than one.
   *
   * Example devicetree fragment:
   *
   *     my-serial: serial@... {
   *             interrupts = < 33 0 >, < 34 1 >;
   *     };
   *
   * Assuming the node's interrupt domain has "#interrupt-cells = <2>;" and
   * the individual cells in each interrupt specifier are named "irq" and
   * "priority" by the node's binding, here are some examples:
   *
   *     #define SERIAL DT_NODELABEL(my_serial)
   *
   *     Example usage                       Value
   *     -------------                       -----
   *     DT_IRQ_BY_IDX(SERIAL, 0, irq)          33
   *     DT_IRQ_BY_IDX(SERIAL, 0, priority)      0
   *     DT_IRQ_BY_IDX(SERIAL, 1, irq,          34
   *     DT_IRQ_BY_IDX(SERIAL, 1, priority)      1
   *
   * @param node_id node identifier
   * @param idx logical index into the interrupt specifier array
   * @param cell cell name specifier
   * @return the named value at the specifier given by the index
   */
  #define DT_IRQ_BY_IDX(node_id, idx, cell)   \
  	DT_CAT(node_id, _IRQ_IDX_##idx##_VAL_##cell)
  
  /**
   * @brief Get a value within an interrupt specifier by name
   *
   * It might help to read the argument order as being similar to
   * "node->interrupts.name.cell".
   *
   * This can be used to get information about an individual interrupt
   * when a device generates more than one, if the bindings give each
   * interrupt specifier a name.
   *
   * @param node_id node identifier
   * @param name lowercase-and-underscores interrupt specifier name
   * @param cell cell name specifier
   * @return the named value at the specifier given by the index
   */
  #define DT_IRQ_BY_NAME(node_id, name, cell) \
  	DT_CAT(node_id, _IRQ_NAME_##name##_VAL_##cell)
  
  /**
   * @brief Get an interrupt specifier's value
   * Equivalent to DT_IRQ_BY_IDX(node_id, 0, cell).
   * @param node_id node identifier
   * @param cell cell name specifier
   * @return the named value at that index
   */
  #define DT_IRQ(node_id, cell) DT_IRQ_BY_IDX(node_id, 0, cell)
  
  /**
   * @brief Get a node's (only) irq number
   *
   * Equivalent to DT_IRQ(node_id, irq). This is provided as a convenience
   * for the common case where a node generates exactly one interrupt,
   * and the IRQ number is in a cell named "irq".
   *
   * @param node_id node identifier
   * @return the interrupt number for the node's only interrupt
   */
  #define DT_IRQN(node_id) DT_IRQ(node_id, irq)
  
  /**
   * @}
   */
  
  /**
   * @defgroup devicetree-generic-chosen Chosen nodes
   * @ingroup devicetree
   * @{
   */
  
  /**
   * @brief Get a node identifier for a /chosen node property
   *
   * This is only valid to call if DT_HAS_CHOSEN(prop) is 1.
   * @param prop lowercase-and-underscores property name for
   *             the /chosen node
   * @return a node identifier for the chosen node property
   */
  #define DT_CHOSEN(prop) DT_CAT(DT_CHOSEN_, prop)
  
  /**
   * @brief Test if the devicetree has a /chosen node
   * @param prop lowercase-and-underscores devicetree property
   * @return 1 if the chosen property exists and refers to a node,
   *         0 otherwise
   */
  #define DT_HAS_CHOSEN(prop) IS_ENABLED(DT_CHOSEN_##prop##_EXISTS)
  
  /**
   * @}
   */
  
  /**
   * @defgroup devicetree-generic-foreach "For-each" macros
   * @ingroup devicetree
   * @{
   */
  
  /**
   * @brief Invokes "fn" for each child of "node_id"
   *
   * The macro "fn" must take one parameter, which will be the node
   * identifier of a child node of "node_id".
   *
   * Example devicetree fragment:
   *
   *     n: node {
   *             child-1 {
   *                     label = "foo";
   *             };
   *             child-2 {
   *                     label = "bar";
   *             };
   *     };
   *
   * Example usage:
   *
   *     #define LABEL_AND_COMMA(node_id) DT_LABEL(node_id),
   *
   *     const char *child_labels[] = {
   *         DT_FOREACH_CHILD(DT_NODELABEL(n), LABEL_AND_COMMA)
   *     };
   *
   * This expands to:
   *
   *     const char *child_labels[] = {
   *         "foo", "bar",
   *     };
   *
   * @param node_id node identifier
   * @param fn macro to invoke
   */
  #define DT_FOREACH_CHILD(node_id, fn) \
  	DT_CAT(node_id, _FOREACH_CHILD)(fn)
  
  /**
   * @brief Invokes "fn" for each child of "node_id" with multiple arguments
   *
   * The macro "fn" takes multiple arguments. The first should be the node
   * identifier for the child node. The remaining are passed-in by the caller.
   *
   * @param node_id node identifier
   * @param fn macro to invoke
   * @param ... variable number of arguments to pass to fn
   *
   * @see DT_FOREACH_CHILD
   */
  #define DT_FOREACH_CHILD_VARGS(node_id, fn, ...) \
  	DT_CAT(node_id, _FOREACH_CHILD_VARGS)(fn, __VA_ARGS__)
  
  /**
   * @brief Call "fn" on the child nodes with status "okay"
   *
   * The macro "fn" should take one argument, which is the node
   * identifier for the child node.
   *
   * As usual, both a missing status and an "ok" status are
   * treated as "okay".
   *
   * @param node_id node identifier
   * @param fn macro to invoke
   */
  #define DT_FOREACH_CHILD_STATUS_OKAY(node_id, fn) \
  	DT_CAT(node_id, _FOREACH_CHILD_STATUS_OKAY)(fn)
  
  /**
   * @brief Call "fn" on the child nodes with status "okay" with multiple
   * arguments
   *
   * The macro "fn" takes multiple arguments. The first should be the node
   * identifier for the child node. The remaining are passed-in by the caller.
   *
   * As usual, both a missing status and an "ok" status are
   * treated as "okay".
   *
   * @param node_id node identifier
   * @param fn macro to invoke
   * @param ... variable number of arguments to pass to fn
   *
   * @see DT_FOREACH_CHILD_STATUS_OKAY
   */
  #define DT_FOREACH_CHILD_STATUS_OKAY_VARGS(node_id, fn, ...) \
  	DT_CAT(node_id, _FOREACH_CHILD_STATUS_OKAY_VARGS)(fn, __VA_ARGS__)
  
  /**
   * @brief Invokes "fn" for each element in the value of property "prop".
   *
   * The macro "fn" must take three parameters: fn(node_id, prop, idx).
   * "node_id" and "prop" are the same as what is passed to
   * DT_FOREACH_PROP_ELEM, and "idx" is the current index into the array.
   * The "idx" values are integer literals starting from 0.
   *
   * Example devicetree fragment:
   *
   *     n: node {
   *             my-ints = <1 2 3>;
   *     };
   *
   * Example usage:
   *
   *     #define TIMES_TWO(node_id, prop, idx) \
   *	       (2 * DT_PROP_BY_IDX(node_id, prop, idx)),
   *
   *     int array[] = {
   *             DT_FOREACH_PROP_ELEM(DT_NODELABEL(n), my_ints, TIMES_TWO)
   *     };
   *
   * This expands to:
   *
   *     int array[] = {
   *             (2 * 1), (2 * 2), (2 * 3),
   *     };
   *
   * In general, this macro expands to:
   *
   *     fn(node_id, prop, 0) fn(node_id, prop, 1) [...] fn(node_id, prop, n-1)
   *
   * where "n" is the number of elements in "prop", as it would be
   * returned by <tt>DT_PROP_LEN(node_id, prop)</tt>.
   *
   * The "prop" argument must refer to a property with type string,
   * array, uint8-array, string-array, phandles, or phandle-array. It is
   * an error to use this macro with properties of other types.
   *
   * @param node_id node identifier
   * @param prop lowercase-and-underscores property name
   * @param fn macro to invoke
   */
  #define DT_FOREACH_PROP_ELEM(node_id, prop, fn)		\
  	DT_CAT4(node_id, _P_, prop, _FOREACH_PROP_ELEM)(fn)
  
  /**
   * @brief Invokes "fn" for each element in the value of property "prop" with
   * multiple arguments.
   *
   * The macro "fn" must take multiple parameters: fn(node_id, prop, idx, ...).
   * "node_id" and "prop" are the same as what is passed to
   * DT_FOREACH_PROP_ELEM, and "idx" is the current index into the array.
   * The "idx" values are integer literals starting from 0. The remaining
   * arguments are passed-in by the caller.
   *
   * @param node_id node identifier
   * @param prop lowercase-and-underscores property name
   * @param fn macro to invoke
   * @param ... variable number of arguments to pass to fn
   *
   * @see DT_FOREACH_PROP_ELEM
   */
  #define DT_FOREACH_PROP_ELEM_VARGS(node_id, prop, fn, ...)		\
  	DT_CAT4(node_id, _P_, prop, _FOREACH_PROP_ELEM_VARGS)(fn, __VA_ARGS__)
  
  /**
   * @brief Call "fn" on all nodes with compatible DT_DRV_COMPAT
   *        and status "okay"
   *
   * This macro expands to:
   *
   *     fn(node_id_1) fn(node_id_2) ... fn(node_id_n)
   *
   * where each "node_id_<i>" is a node identifier for some node with
   * compatible "compat" and status "okay". Whitespace is added between
   * expansions as shown above.
   *
   * Example devicetree fragment:
   *
   *     / {
   *             a {
   *                     compatible = "foo";
   *                     status = "okay";
   *             };
   *             b {
   *                     compatible = "foo";
   *                     status = "disabled";
   *             };
   *             c {
   *                     compatible = "foo";
   *             };
   *     };
   *
   * Example usage:
   *
   *     DT_FOREACH_STATUS_OKAY(foo, DT_NODE_PATH)
   *
   * This expands to one of the following:
   *
   *     "/a" "/c"
   *     "/c" "/a"
   *
   * "One of the following" is because no guarantees are made about the
   * order that node identifiers are passed to "fn" in the expansion.
   *
   * (The "/c" string literal is present because a missing status
   * property is always treated as if the status were set to "okay".)
   *
   * Note also that "fn" is responsible for adding commas, semicolons,
   * or other terminators as needed.
   *
   * @param compat lowercase-and-underscores devicetree compatible
   * @param fn Macro to call for each enabled node. Must accept a
   *           node_id as its only parameter.
   */
  #define DT_FOREACH_STATUS_OKAY(compat, fn)				\
  	COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(compat),			\
  		    (UTIL_CAT(DT_FOREACH_OKAY_, compat)(fn)),	\
  		    ())
  
  /**
   * @brief Invokes "fn" for each status "okay" node of a compatible
   *        with multiple arguments.
   *
   * This is like DT_FOREACH_STATUS_OKAY() except you can also pass
   * additional arguments to "fn".
   *
   * Example devicetree fragment:
   *
   *     / {
   *             a {
   *                     compatible = "foo";
   *                     val = <3>;
   *             };
   *             b {
   *                     compatible = "foo";
   *                     val = <4>;
   *             };
   *     };
   *
   * Example usage:
   *
   *     #define MY_FN(node_id, operator) DT_PROP(node_id, val) operator
   *     x = DT_FOREACH_STATUS_OKAY_VARGS(foo, MY_FN, +) 0;
   *
   * This expands to one of the following:
   *
   *     x = 3 + 4 + 0;
   *     x = 4 + 3 + 0;
   *
   * i.e. it sets x to 7. As with DT_FOREACH_STATUS_OKAY(), there are no
   * guarantees about the order nodes appear in the expansion.
   *
   * @param compat lowercase-and-underscores devicetree compatible
   * @param fn Macro to call for each enabled node. Must accept a
   *           node_id as its only parameter.
   * @param ... Additional arguments to pass to "fn"
   */
  #define DT_FOREACH_STATUS_OKAY_VARGS(compat, fn, ...)			\
  	COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(compat),			\
  		    (UTIL_CAT(DT_FOREACH_OKAY_VARGS_,			\
  			      compat)(fn, __VA_ARGS__)),		\
  		    ())
  
  /**
   * @}
   */
  
  /**
   * @defgroup devicetree-generic-exist Existence checks
   * @ingroup devicetree
   * @{
   */
  
  /**
   * @brief Does a node identifier refer to a node?
   *
   * Tests whether a node identifier refers to a node which exists, i.e.
   * is defined in the devicetree.
   *
   * It doesn't matter whether or not the node has a matching binding,
   * or what the node's status value is. This is purely a check of
   * whether the node exists at all.
   *
   * @param node_id a node identifier
   * @return 1 if the node identifier refers to a node,
   *         0 otherwise.
   */
  #define DT_NODE_EXISTS(node_id) IS_ENABLED(DT_CAT(node_id, _EXISTS))
  
  /**
   * @brief Does a node identifier refer to a node with a status?
   *
   * Example uses:
   *
   *     DT_NODE_HAS_STATUS(DT_PATH(soc, i2c_12340000), okay)
   *     DT_NODE_HAS_STATUS(DT_PATH(soc, i2c_12340000), disabled)
   *
   * Tests whether a node identifier refers to a node which:
   *
   * - exists in the devicetree, and
   * - has a status property matching the second argument
   *   (except that either a missing status or an "ok" status
   *   in the devicetree is treated as if it were "okay" instead)
   *
   * @param node_id a node identifier
   * @param status a status as one of the tokens okay or disabled, not a string
   * @return 1 if the node has the given status, 0 otherwise.
   */
  #define DT_NODE_HAS_STATUS(node_id, status) \
  	DT_NODE_HAS_STATUS_INTERNAL(node_id, status)
  
  /**
   * @brief Does the devicetree have a status "okay" node with a compatible?
   *
   * Test for whether the devicetree has any nodes with status "okay"
   * and the given compatible. That is, this returns 1 if and only if
   * there is at least one "node_id" for which both of these
   * expressions return 1:
   *
   *     DT_NODE_HAS_STATUS(node_id, okay)
   *     DT_NODE_HAS_COMPAT(node_id, compat)
   *
   * As usual, both a missing status and an "ok" status are treated as
   * "okay".
   *
   * @param compat lowercase-and-underscores compatible, without quotes
   * @return 1 if both of the above conditions are met, 0 otherwise
   */
  #define DT_HAS_COMPAT_STATUS_OKAY(compat) \
  	IS_ENABLED(DT_CAT(DT_COMPAT_HAS_OKAY_, compat))
  
  /**
   * @brief Get the number of instances of a given compatible with
   *        status "okay"
   * @param compat lowercase-and-underscores compatible, without quotes
   * @return Number of instances with status "okay"
   */
  #define DT_NUM_INST_STATUS_OKAY(compat)			\
  	UTIL_AND(DT_HAS_COMPAT_STATUS_OKAY(compat),		\
  		 UTIL_CAT(DT_N_INST, DT_DASH(compat, NUM_OKAY)))
  
  /**
   * @brief Does a devicetree node match a compatible?
   *
   * Example devicetree fragment:
   *
   *     n: node {
   *             compatible = "vnd,specific-device", "generic-device";
   *     }
   *
   * Example usages which evaluate to 1:
   *
   *     DT_NODE_HAS_COMPAT(DT_NODELABEL(n), vnd_specific_device)
   *     DT_NODE_HAS_COMPAT(DT_NODELABEL(n), generic_device)
   *
   * This macro only uses the value of the compatible property. Whether
   * or not a particular compatible has a matching binding has no effect
   * on its value, nor does the node's status.
   *
   * @param node_id node identifier
   * @param compat lowercase-and-underscores compatible, without quotes
   * @return 1 if the node's compatible property contains compat,
   *         0 otherwise.
   */
  #define DT_NODE_HAS_COMPAT(node_id, compat) \
  	IS_ENABLED(DT_CAT(node_id, _COMPAT_MATCHES_##compat))
  
  /**
   * @brief Does a devicetree node have a compatible and status?
   *
   * This is equivalent to:
   *
   *     (DT_NODE_HAS_COMPAT(node_id, compat) &&
   *      DT_NODE_HAS_STATUS(node_id, status))
   *
   * @param node_id node identifier
   * @param compat lowercase-and-underscores compatible, without quotes
   * @param status okay or disabled as a token, not a string
   */
  #define DT_NODE_HAS_COMPAT_STATUS(node_id, compat, status) \
  	DT_NODE_HAS_COMPAT(node_id, compat) && DT_NODE_HAS_STATUS(node_id, status)
  
  /**
   * @brief Does a devicetree node have a property?
   *
   * Tests whether a devicetree node has a property defined.
   *
   * This tests whether the property is defined at all, not whether a
   * boolean property is true or false. To get a boolean property's
   * truth value, use DT_PROP(node_id, prop) instead.
   *
   * @param node_id node identifier
   * @param prop lowercase-and-underscores property name
   * @return 1 if the node has the property, 0 otherwise.
   */
  #define DT_NODE_HAS_PROP(node_id, prop) \
  	IS_ENABLED(DT_CAT(node_id, _P_##prop##_EXISTS))
  
  
  /**
   * @brief Does a phandle array have a named cell specifier at an index?
   *
   * If this returns 1, then the phandle-array property "pha" has a cell
   * named "cell" at index "idx", and therefore DT_PHA_BY_IDX(node_id,
   * pha, idx, cell) is valid. If it returns 0, it's an error to use
   * DT_PHA_BY_IDX() with the same arguments.
   *
   * @param node_id node identifier
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param idx index to check within "pha"
   * @param cell lowercase-and-underscores cell name whose existence to check
   *             at index "idx"
   * @return 1 if the named cell exists in the specifier at index idx,
   *         0 otherwise.
   */
  #define DT_PHA_HAS_CELL_AT_IDX(node_id, pha, idx, cell)             \
  	IS_ENABLED(DT_PROP(node_id,                                 \
  			   pha##_IDX_##idx##_VAL_##cell##_EXISTS))
  
  /**
   * @brief Equivalent to DT_PHA_HAS_CELL_AT_IDX(node_id, pha, 0, cell)
   * @param node_id node identifier
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param cell lowercase-and-underscores cell name whose existence to check
   *             at index "idx"
   * @return 1 if the named cell exists in the specifier at index 0,
   *         0 otherwise.
   */
  #define DT_PHA_HAS_CELL(node_id, pha, cell) \
  	DT_PHA_HAS_CELL_AT_IDX(node_id, pha, 0, cell)
  
  /**
   * @}
   */
  
  /**
   * @defgroup devicetree-generic-bus Bus helpers
   * @ingroup devicetree
   * @{
   */
  
  /**
   * @brief Node's bus controller
   *
   * Get the node identifier of the node's bus controller. This can be
   * used with @ref DT_PROP() to get properties of the bus controller.
   *
   * It is an error to use this with nodes which do not have bus
   * controllers.
   *
   * Example devicetree fragment:
   *
   *     i2c@deadbeef {
   *             label = "I2C_CTLR";
   *             status = "okay";
   *             clock-frequency = < 100000 >;
   *
   *             i2c_device: accelerometer@12 {
   *                     ...
   *             };
   *     };
   *
   * Example usage:
   *
   *     DT_PROP(DT_BUS(DT_NODELABEL(i2c_device)), clock_frequency) // 100000
   *
   * @param node_id node identifier
   * @return a node identifier for the node's bus controller
   */
  #define DT_BUS(node_id) DT_CAT(node_id, _BUS)
  
  /**
   * @brief Node's bus controller's label property
   * @param node_id node identifier
   * @return the label property of the node's bus controller DT_BUS(node)
   */
  #define DT_BUS_LABEL(node_id) DT_PROP(DT_BUS(node_id), label)
  
  /**
   * @brief Is a node on a bus of a given type?
   *
   * Example devicetree overlay:
   *
   *     &i2c0 {
   *            temp: temperature-sensor@76 {
   *                     compatible = "vnd,some-sensor";
   *                     reg = <0x76>;
   *            };
   *     };
   *
   * Example usage, assuming "i2c0" is an I2C bus controller node, and
   * therefore "temp" is on an I2C bus:
   *
   *     DT_ON_BUS(DT_NODELABEL(temp), i2c) // 1
   *     DT_ON_BUS(DT_NODELABEL(temp), spi) // 0
   *
   * @param node_id node identifier
   * @param bus lowercase-and-underscores bus type as a C token (i.e.
   *            without quotes)
   * @return 1 if the node is on a bus of the given type,
   *         0 otherwise
   */
  #define DT_ON_BUS(node_id, bus) IS_ENABLED(DT_CAT(node_id, _BUS_##bus))
  
  /**
   * @}
   */
  
  /**
   * @defgroup devicetree-inst Instance-based devicetree APIs
   * @ingroup devicetree
   * @{
   */
  
  /**
   * @brief Node identifier for an instance of a DT_DRV_COMPAT compatible
   * @param inst instance number
   * @return a node identifier for the node with DT_DRV_COMPAT compatible and
   *         instance number "inst"
   */
  #define DT_DRV_INST(inst) DT_INST(inst, DT_DRV_COMPAT)
  
  /**
   * @brief Call "fn" on all child nodes of DT_DRV_INST(inst).
   *
   * The macro "fn" should take one argument, which is the node
   * identifier for the child node.
   *
   * @param inst instance number
   * @param fn macro to invoke on each child node identifier
   *
   * @see DT_FOREACH_CHILD
   */
  #define DT_INST_FOREACH_CHILD(inst, fn) \
  	DT_FOREACH_CHILD(DT_DRV_INST(inst), fn)
  
  /**
   * @brief Call "fn" on all child nodes of DT_DRV_INST(inst).
   *
   * The macro "fn" takes multiple arguments. The first should be the node
   * identifier for the child node. The remaining are passed-in by the caller.
   *
   * @param inst instance number
   * @param fn macro to invoke on each child node identifier
   * @param ... variable number of arguments to pass to fn
   *
   * @see DT_FOREACH_CHILD
   */
  #define DT_INST_FOREACH_CHILD_VARGS(inst, fn, ...) \
  	DT_FOREACH_CHILD_VARGS(DT_DRV_INST(inst), fn, __VA_ARGS__)
  
  /**
   * @brief Get a DT_DRV_COMPAT instance property
   * @param inst instance number
   * @param prop lowercase-and-underscores property name
   * @return a representation of the property's value
   */
  #define DT_INST_PROP(inst, prop) DT_PROP(DT_DRV_INST(inst), prop)
  
  /**
   * @brief Get a DT_DRV_COMPAT property length
   * @param inst instance number
   * @param prop lowercase-and-underscores property name
   * @return logical length of the property
   */
  #define DT_INST_PROP_LEN(inst, prop) DT_PROP_LEN(DT_DRV_INST(inst), prop)
  
  /**
   * @brief Is index "idx" valid for an array type property
   *        on a DT_DRV_COMPAT instance?
   * @param inst instance number
   * @param prop lowercase-and-underscores property name
   * @param idx index to check
   * @return 1 if "idx" is a valid index into the given property,
   *         0 otherwise.
   */
  #define DT_INST_PROP_HAS_IDX(inst, prop, idx) \
  	DT_PROP_HAS_IDX(DT_DRV_INST(inst), prop, idx)
  
  /**
   * @brief Get a DT_DRV_COMPAT element value in an array property
   * @param inst instance number
   * @param prop lowercase-and-underscores property name
   * @param idx the index to get
   * @return a representation of the idx-th element of the property
   */
  #define DT_INST_PROP_BY_IDX(inst, prop, idx) \
  	DT_PROP_BY_IDX(DT_DRV_INST(inst), prop, idx)
  
  /**
   * @brief Like DT_INST_PROP(), but with a fallback to default_value
   * @param inst instance number
   * @param prop lowercase-and-underscores property name
   * @param default_value a fallback value to expand to
   * @return DT_INST_PROP(inst, prop) or default_value
   */
  #define DT_INST_PROP_OR(inst, prop, default_value) \
  	DT_PROP_OR(DT_DRV_INST(inst), prop, default_value)
  
  /**
   * @brief Get a DT_DRV_COMPAT instance's "label" property
   * @param inst instance number
   * @return instance's label property value
   */
  #define DT_INST_LABEL(inst) DT_INST_PROP(inst, label)
  
  /**
   * @brief Get a DT_DRV_COMPAT instance's property value from a phandle's node
   * @param inst instance number
   * @param ph lowercase-and-underscores property of "inst"
   *           with type "phandle"
   * @param prop lowercase-and-underscores property of the phandle's node
   * @return the value of "prop" as described in the DT_PROP() documentation
   */
  #define DT_INST_PROP_BY_PHANDLE(inst, ph, prop) \
  	DT_INST_PROP_BY_PHANDLE_IDX(inst, ph, 0, prop)
  
  /**
   * @brief Get a DT_DRV_COMPAT instance's property value from a phandle in a
   * property.
   * @param inst instance number
   * @param phs lowercase-and-underscores property with type "phandle",
   *            "phandles", or "phandle-array"
   * @param idx logical index into "phs", which must be zero if "phs"
   *            has type "phandle"
   * @param prop lowercase-and-underscores property of the phandle's node
   * @return the value of "prop" as described in the DT_PROP() documentation
   */
  #define DT_INST_PROP_BY_PHANDLE_IDX(inst, phs, idx, prop) \
  	DT_PROP_BY_PHANDLE_IDX(DT_DRV_INST(inst), phs, idx, prop)
  
  /**
   * @brief Get a DT_DRV_COMPAT instance's phandle-array specifier value at an index
   * @param inst instance number
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param idx logical index into the property "pha"
   * @param cell binding's cell name within the specifier at index "idx"
   * @return the value of the cell inside the specifier at index "idx"
   */
  #define DT_INST_PHA_BY_IDX(inst, pha, idx, cell) \
  	DT_PHA_BY_IDX(DT_DRV_INST(inst), pha, idx, cell)
  
  /**
   * @brief Like DT_INST_PHA_BY_IDX(), but with a fallback to default_value
   * @param inst instance number
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param idx logical index into the property "pha"
   * @param cell binding's cell name within the specifier at index "idx"
   * @param default_value a fallback value to expand to
   * @return DT_INST_PHA_BY_IDX(inst, pha, idx, cell) or default_value
   */
  #define DT_INST_PHA_BY_IDX_OR(inst, pha, idx, cell, default_value) \
  	DT_PHA_BY_IDX_OR(DT_DRV_INST(inst), pha, idx, cell, default_value)
  
  /**
   * @brief Get a DT_DRV_COMPAT instance's phandle-array specifier value
   * Equivalent to DT_INST_PHA_BY_IDX(inst, pha, 0, cell)
   * @param inst instance number
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param cell binding's cell name for the specifier at "pha" index 0
   * @return the cell value
   */
  #define DT_INST_PHA(inst, pha, cell) DT_INST_PHA_BY_IDX(inst, pha, 0, cell)
  
  /**
   * @brief Like DT_INST_PHA(), but with a fallback to default_value
   * @param inst instance number
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param cell binding's cell name for the specifier at "pha" index 0
   * @param default_value a fallback value to expand to
   * @return DT_INST_PHA(inst, pha, cell) or default_value
   */
  #define DT_INST_PHA_OR(inst, pha, cell, default_value) \
  	DT_INST_PHA_BY_IDX_OR(inst, pha, 0, cell, default_value)
  
  /**
   * @brief Get a DT_DRV_COMPAT instance's value within a phandle-array
   * specifier by name
   * @param inst instance number
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param name lowercase-and-underscores name of a specifier in "pha"
   * @param cell binding's cell name for the named specifier
   * @return the cell value
   */
  #define DT_INST_PHA_BY_NAME(inst, pha, name, cell) \
  	DT_PHA_BY_NAME(DT_DRV_INST(inst), pha, name, cell)
  
  /**
   * @brief Like DT_INST_PHA_BY_NAME(), but with a fallback to default_value
   * @param inst instance number
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param name lowercase-and-underscores name of a specifier in "pha"
   * @param cell binding's cell name for the named specifier
   * @param default_value a fallback value to expand to
   * @return DT_INST_PHA_BY_NAME(inst, pha, name, cell) or default_value
   */
  #define DT_INST_PHA_BY_NAME_OR(inst, pha, name, cell, default_value) \
  	DT_PHA_BY_NAME_OR(DT_DRV_INST(inst), pha, name, cell, default_value)
  
  /**
   * @brief Get a DT_DRV_COMPAT instance's phandle node identifier from a
   * phandle array by name
   * @param inst instance number
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param name lowercase-and-underscores name of an element in "pha"
   * @return node identifier for the phandle at the element named "name"
   */
  #define DT_INST_PHANDLE_BY_NAME(inst, pha, name) \
  	DT_PHANDLE_BY_NAME(DT_DRV_INST(inst), pha, name) \
  
  /**
   * @brief Get a DT_DRV_COMPAT instance's node identifier for a phandle in
   * a property.
   * @param inst instance number
   * @param prop lowercase-and-underscores property name in "inst"
   *             with type "phandle", "phandles" or "phandle-array"
   * @param idx index into "prop"
   * @return a node identifier for the phandle at index "idx" in "prop"
   */
  #define DT_INST_PHANDLE_BY_IDX(inst, prop, idx) \
  	DT_PHANDLE_BY_IDX(DT_DRV_INST(inst), prop, idx)
  
  /**
   * @brief Get a DT_DRV_COMPAT instance's node identifier for a phandle
   * property's value
   * @param inst instance number
   * @param prop lowercase-and-underscores property of "inst"
   *             with type "phandle"
   * @return a node identifier for the node pointed to by "ph"
   */
  #define DT_INST_PHANDLE(inst, prop) DT_INST_PHANDLE_BY_IDX(inst, prop, 0)
  
  /**
   * @brief is "idx" a valid register block index on a DT_DRV_COMPAT instance?
   * @param inst instance number
   * @param idx index to check
   * @return 1 if "idx" is a valid register block index,
   *         0 otherwise.
   */
  #define DT_INST_REG_HAS_IDX(inst, idx) DT_REG_HAS_IDX(DT_DRV_INST(inst), idx)
  
  /**
   * @brief Get a DT_DRV_COMPAT instance's idx-th register block's address
   * @param inst instance number
   * @param idx index of the register whose address to return
   * @return address of the instance's idx-th register block
   */
  #define DT_INST_REG_ADDR_BY_IDX(inst, idx) DT_REG_ADDR_BY_IDX(DT_DRV_INST(inst), idx)
  
  /**
   * @brief Get a DT_DRV_COMPAT instance's idx-th register block's size
   * @param inst instance number
   * @param idx index of the register whose size to return
   * @return size of the instance's idx-th register block
   */
  #define DT_INST_REG_SIZE_BY_IDX(inst, idx) \
  	DT_REG_SIZE_BY_IDX(DT_DRV_INST(inst), idx)
  
  /**
   * @brief Get a DT_DRV_COMPAT's register block address by name
   * @param inst instance number
   * @param name lowercase-and-underscores register specifier name
   * @return address of the register block with the given name
   */
  #define DT_INST_REG_ADDR_BY_NAME(inst, name) \
  	DT_REG_ADDR_BY_NAME(DT_DRV_INST(inst), name)
  
  /**
   * @brief Get a DT_DRV_COMPAT's register block size by name
   * @param inst instance number
   * @param name lowercase-and-underscores register specifier name
   * @return size of the register block with the given name
   */
  #define DT_INST_REG_SIZE_BY_NAME(inst, name) \
  	DT_REG_SIZE_BY_NAME(DT_DRV_INST(inst), name)
  
  /**
   * @brief Get a DT_DRV_COMPAT's (only) register block address
   * @param inst instance number
   * @return instance's register block address
   */
  #define DT_INST_REG_ADDR(inst) DT_INST_REG_ADDR_BY_IDX(inst, 0)
  
  /**
   * @brief Get a DT_DRV_COMPAT's (only) register block size
   * @param inst instance number
   * @return instance's register block size
   */
  #define DT_INST_REG_SIZE(inst) DT_INST_REG_SIZE_BY_IDX(inst, 0)
  
  /**
   * @brief Get a DT_DRV_COMPAT interrupt specifier value at an index
   * @param inst instance number
   * @param idx logical index into the interrupt specifier array
   * @param cell cell name specifier
   * @return the named value at the specifier given by the index
   */
  #define DT_INST_IRQ_BY_IDX(inst, idx, cell) \
  	DT_IRQ_BY_IDX(DT_DRV_INST(inst), idx, cell)
  
  /**
   * @brief Get a DT_DRV_COMPAT interrupt specifier value by name
   * @param inst instance number
   * @param name lowercase-and-underscores interrupt specifier name
   * @param cell cell name specifier
   * @return the named value at the specifier given by the index
   */
  #define DT_INST_IRQ_BY_NAME(inst, name, cell) \
  	DT_IRQ_BY_NAME(DT_DRV_INST(inst), name, cell)
  
  /**
   * @brief Get a DT_DRV_COMPAT interrupt specifier's value
   * @param inst instance number
   * @param cell cell name specifier
   * @return the named value at that index
   */
  #define DT_INST_IRQ(inst, cell) DT_INST_IRQ_BY_IDX(inst, 0, cell)
  
  /**
   * @brief Get a DT_DRV_COMPAT's (only) irq number
   * @param inst instance number
   * @return the interrupt number for the node's only interrupt
   */
  #define DT_INST_IRQN(inst) DT_INST_IRQ(inst, irq)
  
  /**
   * @brief Get a DT_DRV_COMPAT's bus node identifier
   * @param inst instance number
   * @return node identifier for the instance's bus node
   */
  #define DT_INST_BUS(inst) DT_BUS(DT_DRV_INST(inst))
  
  /**
   * @brief Get a DT_DRV_COMPAT's bus node's label property
   * @param inst instance number
   * @return the label property of the instance's bus controller
   */
  #define DT_INST_BUS_LABEL(inst) DT_BUS_LABEL(DT_DRV_INST(inst))
  
  /**
   * @brief Test if a DT_DRV_COMPAT's bus type is a given type
   * @param inst instance number
   * @param bus a binding's bus type as a C token, lowercased and without quotes
   * @return 1 if the given instance is on a bus of the given type,
   *         0 otherwise
   */
  #define DT_INST_ON_BUS(inst, bus) DT_ON_BUS(DT_DRV_INST(inst), bus)
  
  /**
   * @brief Test if any DT_DRV_COMPAT node is on a bus of a given type
   *        and has status okay
   *
   * This is a special-purpose macro which can be useful when writing
   * drivers for devices which can appear on multiple buses. One example
   * is a sensor device which may be wired on an I2C or SPI bus.
   *
   * Example devicetree overlay:
   *
   *     &i2c0 {
   *            temp: temperature-sensor@76 {
   *                     compatible = "vnd,some-sensor";
   *                     reg = <0x76>;
   *            };
   *     };
   *
   * Example usage, assuming "i2c0" is an I2C bus controller node, and
   * therefore "temp" is on an I2C bus:
   *
   *     #define DT_DRV_COMPAT vnd_some_sensor
   *
   *     DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) // 1
   *
   * @param bus a binding's bus type as a C token, lowercased and without quotes
   * @return 1 if any enabled node with that compatible is on that bus type,
   *         0 otherwise
   */
  #define DT_ANY_INST_ON_BUS_STATUS_OKAY(bus) \
  	DT_COMPAT_ON_BUS_INTERNAL(DT_DRV_COMPAT, bus)
  
  /**
   * @brief Call "fn" on all nodes with compatible DT_DRV_COMPAT
   *        and status "okay"
   *
   * This macro calls "fn(inst)" on each "inst" number that refers to a
   * node with status "okay". Whitespace is added between invocations.
   *
   * Example devicetree fragment:
   *
   *     a {
   *             compatible = "vnd,device";
   *             status = "okay";
   *             label = "DEV_A";
   *     };
   *
   *     b {
   *             compatible = "vnd,device";
   *             status = "okay";
   *             label = "DEV_B";
   *     };
   *
   *     c {
   *             compatible = "vnd,device";
   *             status = "disabled";
   *             label = "DEV_C";
   *     };
   *
   * Example usage:
   *
   *     #define DT_DRV_COMPAT vnd_device
   *     #define MY_FN(inst) DT_INST_LABEL(inst),
   *
   *     DT_INST_FOREACH_STATUS_OKAY(MY_FN)
   *
   * This expands to:
   *
   *     MY_FN(0) MY_FN(1)
   *
   * and from there, to either this:
   *
   *     "DEV_A", "DEV_B",
   *
   * or this:
   *
   *     "DEV_B", "DEV_A",
   *
   * No guarantees are made about the order that a and b appear in the
   * expansion.
   *
   * Note that "fn" is responsible for adding commas, semicolons, or
   * other separators or terminators.
   *
   * Device drivers should use this macro whenever possible to
   * instantiate a struct device for each enabled node in the devicetree
   * of the driver's compatible DT_DRV_COMPAT.
   *
   * @param fn Macro to call for each enabled node. Must accept an
   *           instance number as its only parameter.
   */
  #define DT_INST_FOREACH_STATUS_OKAY(fn) \
  	COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT),	\
  		    (UTIL_CAT(DT_FOREACH_OKAY_INST_,		\
  			      DT_DRV_COMPAT)(fn)),		\
  		    ())
  
  /**
   * @brief Call "fn" on all nodes with compatible DT_DRV_COMPAT
   *        and status "okay" with multiple arguments
   *
   *
   * @param fn Macro to call for each enabled node. Must accept an
   *           instance number as its only parameter.
   * @param ... variable number of arguments to pass to fn
   *
   * @see DT_INST_FOREACH_STATUS_OKAY
   */
  #define DT_INST_FOREACH_STATUS_OKAY_VARGS(fn, ...) \
  	COND_CODE_1(DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT),	\
  		    (UTIL_CAT(DT_FOREACH_OKAY_INST_VARGS_,	\
  			      DT_DRV_COMPAT)(fn, __VA_ARGS__)),	\
  		    ())
  
  /**
   * @brief Invokes "fn" for each element of property "prop" for
   *        a DT_DRV_COMPAT instance.
   *
   * Equivalent to DT_FOREACH_PROP_ELEM(DT_DRV_INST(inst), prop, fn).
   *
   * @param inst instance number
   * @param prop lowercase-and-underscores property name
   * @param fn macro to invoke
   */
  #define DT_INST_FOREACH_PROP_ELEM(inst, prop, fn) \
  	DT_FOREACH_PROP_ELEM(DT_DRV_INST(inst), prop, fn)
  
  /**
   * @brief Invokes "fn" for each element of property "prop" for
   *        a DT_DRV_COMPAT instance with multiple arguments.
   *
   * Equivalent to
   *      DT_FOREACH_PROP_ELEM_VARGS(DT_DRV_INST(inst), prop, fn, __VA_ARGS__)
   *
   * @param inst instance number
   * @param prop lowercase-and-underscores property name
   * @param fn macro to invoke
   * @param ... variable number of arguments to pass to fn
   *
   * @see DT_INST_FOREACH_PROP_ELEM
   */
  #define DT_INST_FOREACH_PROP_ELEM_VARGS(inst, prop, fn, ...) \
  	DT_FOREACH_PROP_ELEM_VARGS(DT_DRV_INST(inst), prop, fn, __VA_ARGS__)
  
  /**
   * @brief Does a DT_DRV_COMPAT instance have a property?
   * @param inst instance number
   * @param prop lowercase-and-underscores property name
   * @return 1 if the instance has the property, 0 otherwise.
   */
  #define DT_INST_NODE_HAS_PROP(inst, prop) \
  	DT_NODE_HAS_PROP(DT_DRV_INST(inst), prop)
  
  /**
   * @brief Does a phandle array have a named cell specifier at an index
   *        for a DT_DRV_COMPAT instance?
   * @param inst instance number
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param idx index to check
   * @param cell named cell value whose existence to check
   * @return 1 if the named cell exists in the specifier at index idx,
   *         0 otherwise.
   */
  #define DT_INST_PHA_HAS_CELL_AT_IDX(inst, pha, idx, cell) \
  	DT_PHA_HAS_CELL_AT_IDX(DT_DRV_INST(inst), pha, idx, cell)
  
  /**
   * @brief Does a phandle array have a named cell specifier at index 0
   *        for a DT_DRV_COMPAT instance?
   * @param inst instance number
   * @param pha lowercase-and-underscores property with type "phandle-array"
   * @param cell named cell value whose existence to check
   * @return 1 if the named cell exists in the specifier at index 0,
   *         0 otherwise.
   */
  #define DT_INST_PHA_HAS_CELL(inst, pha, cell) \
  	DT_INST_PHA_HAS_CELL_AT_IDX(inst, pha, 0, cell)
  
  /**
   * @brief is index valid for interrupt property on a DT_DRV_COMPAT instance?
   * @param inst instance number
   * @param idx logical index into the interrupt specifier array
   * @return 1 if the idx is valid for the interrupt property
   *         0 otherwise.
   */
  #define DT_INST_IRQ_HAS_IDX(inst, idx) DT_IRQ_HAS_IDX(DT_DRV_INST(inst), idx)
  
  /**
   * @brief Does a DT_DRV_COMPAT instance have an interrupt named cell specifier?
   * @param inst instance number
   * @param idx index to check
   * @param cell named cell value whose existence to check
   * @return 1 if the named cell exists in the interrupt specifier at index idx
   *         0 otherwise.
   */
  #define DT_INST_IRQ_HAS_CELL_AT_IDX(inst, idx, cell) \
  	DT_IRQ_HAS_CELL_AT_IDX(DT_DRV_INST(inst), idx, cell)
  
  /**
   * @brief Does a DT_DRV_COMPAT instance have an interrupt value?
   * @param inst instance number
   * @param cell named cell value whose existence to check
   * @return 1 if the named cell exists in the interrupt specifier at index 0
   *         0 otherwise.
   */
  #define DT_INST_IRQ_HAS_CELL(inst, cell) \
  	DT_INST_IRQ_HAS_CELL_AT_IDX(inst, 0, cell)
  
  /**
   * @brief Does a DT_DRV_COMPAT instance have an interrupt value?
   * @param inst instance number
   * @param name lowercase-and-underscores interrupt specifier name
   * @return 1 if "name" is a valid named specifier
   */
  #define DT_INST_IRQ_HAS_NAME(inst, name) \
  	DT_IRQ_HAS_NAME(DT_DRV_INST(inst), name)
  
  /**
   * @}
   */
  
  /** @internal pay no attention to the man behind the curtain! */
  #define DT_PATH_INTERNAL(...) \
  	UTIL_CAT(DT_ROOT, MACRO_MAP_CAT(DT_S_PREFIX, __VA_ARGS__))
  /** @internal helper for DT_PATH(): prepends _S_ to a node name */
  #define DT_S_PREFIX(name) _S_##name
  
  /**
   * @internal concatenation helper, 2 arguments
   *
   * This and the following macros are used to paste things together
   * with "##" *after* forcing expansion on each argument.
   *
   * We could try to use something like UTIL_CAT(), but the compiler
   * error messages from the util macros can be extremely long when they
   * are misused. This unfortunately happens often with devicetree.h,
   * since its macro-based API is fiddly and can be hard to get right.
   *
   * Keeping things brutally simple here hopefully makes some errors
   * easier to read.
   */
  #define DT_CAT(a1, a2) a1 ## a2
  /** @internal concatenation helper, 3 arguments */
  #define DT_CAT3(a1, a2, a3) a1 ## a2 ## a3
  /** @internal concatenation helper, 4 arguments */
  #define DT_CAT4(a1, a2, a3, a4) a1 ## a2 ## a3 ## a4
  /** @internal concatenation helper, 5 arguments */
  #define DT_CAT5(a1, a2, a3, a4, a5) a1 ## a2 ## a3 ## a4 ## a5
  /** @internal concatenation helper, 6 arguments */
  #define DT_CAT6(a1, a2, a3, a4, a5, a6) a1 ## a2 ## a3 ## a4 ## a5 ## a6
  /*
   * If you need to define a bigger DT_CATN(), do so here. Don't leave
   * any "holes" of undefined macros, please.
   */
  
  /** @internal helper for node identifier macros to expand args */
  #define DT_DASH(...) MACRO_MAP_CAT(DT_DASH_PREFIX, __VA_ARGS__)
  /** @internal helper for DT_DASH(): prepends _ to a name */
  #define DT_DASH_PREFIX(name) _##name
  /** @internal helper for DT_NODE_HAS_STATUS */
  #define DT_NODE_HAS_STATUS_INTERNAL(node_id, status) \
  	IS_ENABLED(DT_CAT(node_id, _STATUS_ ## status))
  /** @internal helper for test cases and DT_ANY_INST_ON_BUS_STATUS_OKAY() */
  #define DT_COMPAT_ON_BUS_INTERNAL(compat, bus) \
  	IS_ENABLED(UTIL_CAT(DT_CAT(DT_COMPAT_, compat), _BUS_##bus))
  
  /* have these last so they have access to all previously defined macros */
  #include <devicetree/io-channels.h>
  #include <devicetree/clocks.h>
  #include <devicetree/gpio.h>
  #include <devicetree/spi.h>
  #include <devicetree/dma.h>
  #include <devicetree/pwms.h>
  #include <devicetree/fixed-partitions.h>
  #include <devicetree/zephyr.h>
  #include <devicetree/ordinals.h>
  
  #endif /* DEVICETREE_H */
  