Skip to content

devicetree: allow interating devicetree nodes with identical zephyr,device-type properties #67698

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions doc/build/dts/macros.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ node-macro = property-macro
node-macro =/ pinctrl-macro
; A macro about the GPIO hog properties in a node.
node-macro =/ gpiohogs-macro
; A macro about the zephyr,device-type property in a node.
node-macro =/ devicetype-macro
; EXISTS macro: node exists in the devicetree
node-macro =/ %s"DT_N" path-id %s"_EXISTS"
; Bus macros: the plain BUS is a way to access a node's bus controller.
Expand Down Expand Up @@ -222,6 +224,42 @@ gpiohogs-macro =/ %s"DT_N" path-id %s"_GPIO_HOGS_IDX_" DIGIT %s"_VAL_flags_EXIST
; #define DT_N_<node-2 path>_GPIO_HOGS_IDX_0_VAL_flags 0x30
gpiohogs-macro =/ %s"DT_N" path-id %s"_GPIO_HOGS_IDX_" DIGIT %s"_VAL_flags"

; --------------------------------------------------------------------
; devicetype-macro: a macro related to zephyr,device-type properties
;
; The following examples assume something like this:
;
; node-1: gpio@... {
; compatible = "vnd,gpio";
; };
;
; node-2: adc@... {
; compatible = "vnd,adc";
; };
;
; Bindings fragment for the vnd,gpio compatible:
;
; zephyr,device-type:
; default: gpio-controller
; const: gpio-controller
;
; Bindings fragment for the vnd,adc compatible:
;
; zephyr,device-type:
; default: adc-controller
; const: adc-controller

; The node contains a zephyr,device-type property.
;
; #define DT_N_<node-1 path>_ZEPHYR_DEVICE_TYPE_EXISTS 1
; #define DT_N_<node-2 path>_ZEPHYR_DEVICE_TYPE_EXISTS 1
devicetype-macro = %s"DT_N" path-id %s"_ZEPHYR_DEVICE_TYPE_EXISTS"
; The node contains a zephyr,device-type property with the given device type.
;
; #define DT_N_<node-1 path>_ZEPHYR_DEVICE_TYPE_gpio_controller 1
; #define DT_N_<node-2 path>_ZEPHYR_DEVICE_TYPE_adc_controller 1
devicetypes-macro =/ %s"DT_N" path-id %s"_ZEPHYR_DEVICE_TYPE_" device-type

; --------------------------------------------------------------------
; property-macro: a macro related to a node property
;
Expand Down
69 changes: 1 addition & 68 deletions drivers/adc/adc_shell.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#include <zephyr/sys/util.h>
#include <zephyr/devicetree.h>


#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(adc_shell);
Expand Down Expand Up @@ -74,73 +73,7 @@ static struct adc_hdl {
struct adc_channel_cfg channel_config;
uint8_t resolution;
} adc_list[] = {
/* zephyr-keep-sorted-start */
DT_FOREACH_STATUS_OKAY(adi_ad559x_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(atmel_sam0_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(atmel_sam_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(atmel_sam_afec, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(espressif_esp32_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(gd_gd32_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(infineon_cat1_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(infineon_xmc4xxx_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ite_it8xxx2_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(lltc_ltc2451, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(maxim_max11102, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(maxim_max11103, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(maxim_max11105, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(maxim_max11106, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(maxim_max11110, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(maxim_max11111, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(maxim_max11115, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(maxim_max11116, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(maxim_max11117, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(maxim_max11253, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(maxim_max11254, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(microchip_mcp3204, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(microchip_mcp3208, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(microchip_xec_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(nordic_nrf_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(nordic_nrf_saadc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(nuvoton_npcx_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(nuvoton_numaker_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(nxp_kinetis_adc12, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(nxp_kinetis_adc16, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(nxp_lpc_lpadc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(nxp_mcux_12b1msps_sar, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(nxp_s32_adc_sar, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(nxp_vf610_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(raspberrypi_pico_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(renesas_smartbond_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(renesas_smartbond_sdadc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(silabs_gecko_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(silabs_gecko_iadc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(st_stm32_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(st_stm32f1_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(st_stm32f4_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(telink_b91_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_ads1013, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_ads1014, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_ads1015, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_ads1112, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_ads1113, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_ads1114, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_ads1115, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_ads1119, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_ads114s08, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_ads7052, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_cc13xx_cc26xx_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_cc32xx_adc, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_lmp90077, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_lmp90078, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_lmp90079, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_lmp90080, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_lmp90097, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_lmp90098, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_lmp90099, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_lmp90100, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(ti_tla2021, ADC_HDL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(zephyr_adc_emul, ADC_HDL_LIST_ENTRY)
/* zephyr-keep-sorted-stop */
DT_FOREACH_DEVICE_TYPE_STATUS_OKAY_NODE(adc_controller, ADC_HDL_LIST_ENTRY)
};

static struct adc_hdl *get_adc(const char *device_label)
Expand Down
4 changes: 4 additions & 0 deletions dts/bindings/adc/adc-controller.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
include: base.yaml

properties:
zephyr,device-type:
default: adc-controller
const: adc-controller

"#io-channel-cells":
type: int
required: true
Expand Down
4 changes: 4 additions & 0 deletions dts/bindings/base/base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
include: [pm.yaml]

properties:
zephyr,device-type:
type: string
description: indicates the device type.
Comment on lines +6 to +8
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry, but to me, using devicetree to add yet more software properties is a -1. Usage of zephyr, has become a mess to a point where we should likely rename devicetree to something else, maybe ztree or something. This solution also doesn't account for device nodes that can have >1 device type, which even if not supported now, they should if we ever pretend to fully align with Linux.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding that my suggestion just above would theoretically be able to handle multiple types


status:
type: string
description: indicates the operational status of a device
Expand Down
5 changes: 5 additions & 0 deletions dts/bindings/gpio/gpio-controller.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@

# Common fields for GPIO controllers

include: base.yaml

properties:
zephyr,device-type:
default: gpio-controller
const: gpio-controller
"gpio-controller":
type: boolean
required: true
Expand Down
1 change: 1 addition & 0 deletions include/zephyr/devicetree.h
Original file line number Diff line number Diff line change
Expand Up @@ -4523,6 +4523,7 @@
/** @endcond */

/* have these last so they have access to all previously defined macros */
#include <zephyr/devicetree/device_type.h>
#include <zephyr/devicetree/io-channels.h>
#include <zephyr/devicetree/clocks.h>
#include <zephyr/devicetree/gpio.h>
Expand Down
145 changes: 145 additions & 0 deletions include/zephyr/devicetree/device_type.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* Copyright (c) 2024 Vestas Wind Systems A/S
*
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @file
* @brief Devicetree device type
*/

#ifndef ZEPHYR_INCLUDE_DEVICETREE_DEVICE_TYPE_H_
#define ZEPHYR_INCLUDE_DEVICETREE_DEVICE_TYPE_H_

#include <zephyr/devicetree.h>
#include <zephyr/sys/util_macro.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* @defgroup devicetree-device-type Devicetree device type
* @ingroup devicetree
* @{
*/

/**
* @brief Does a devicetree node have a zephyr,device-type property?
*
* Tests whether a devicetree node has a zephyr,device-type property defined.
*
* @param node_id node identifier
* @return 1 if the node has the zephyr,device-type property, 0 otherwise.
*/
#define DT_NODE_HAS_DEVICE_TYPE(node_id) DT_NODE_HAS_PROP(node_id, zephyr_device_type)

/**
* @brief Does a devicetree node have a matching zephyr,device-type property?
*
* Tests whether a devicetree node has a zephyr,device-type property matching @p device_type.
*
* @param node_id node identifier
* @param device_type lowercase-and-underscores device type
* @return 1 if the node has a matching zephyr,device-type property, 0 otherwise.
*/
#define DT_NODE_IS_DEVICE_TYPE(node_id, device_type) \
IS_ENABLED(DT_CAT3(node_id, _ZEPHYR_DEVICE_TYPE_, device_type))

/**
* @brief Does a devicetree node have a matching zephyr,device-type property and status `okay`?
*
* Tests whether a devicetree node has a zephyr,device-type property matching @p device_type and
* status `okay` (as usual, a missing status property is treated as status `okay`).
*
* @param node_id node identifier
* @param device_type lowercase-and-underscores device type
* @return 1 if the node has a matching zephyr,device-type property and status `okay`, 0 otherwise.
*/
#define DT_NODE_IS_DEVICE_TYPE_STATUS_OKAY(node_id, device_type) \
UTIL_AND(DT_NODE_IS_DEVICE_TYPE(node_id, device_type), DT_NODE_HAS_STATUS(node_id, okay))

/**
* @cond INTERNAL_HIDDEN
*/

#define DT_FOREACH_DEVICE_TYPE_NODE_INTERNAL(node_id, device_type, fn) \
COND_CODE_1(DT_NODE_IS_DEVICE_TYPE(node_id, device_type), (fn(node_id)), ())

#define DT_FOREACH_DEVICE_TYPE_NODE_VARGS_INTERNAL(node_id, device_type, fn, ...) \
COND_CODE_1(DT_NODE_IS_DEVICE_TYPE(node_id, device_type), (fn(node_id, __VA_ARGS__)), ())

/**
* INTERNAL_HIDDEN @endcond
*/

/**
* @brief Invokes @p fn for every node in the tree with a ``zephyr,device-type`` property matching
* @p device_type.
*
* The macro @p fn must take one parameter, which will be a node identifier. The macro is expanded
* once for each node in the tree with a ``zephyr,device-type`` property matching @p
* device_type. The order that nodes are visited in is not specified.
*
* @param device_type lowercase-and-underscores device type
* @param fn macro to invoke
*/
#define DT_FOREACH_DEVICE_TYPE_NODE(device_type, fn) \
DT_FOREACH_NODE_VARGS(DT_FOREACH_DEVICE_TYPE_NODE_INTERNAL, device_type, fn)

/**
* @brief Invokes @p fn for every node in the tree with a ``zephyr,device-type`` property matching
* @p device_type with multiple arguments.
*
* The macro @p fn takes multiple arguments. The first should be the node identifier for the
* node. The macro is expanded once for each node in the tree with a ``zephyr,device-type`` property
* matching @p device_type. The order that nodes are visited in is not specified.
*
* @param device_type lowercase-and-underscores device type
* @param fn macro to invoke
*/
#define DT_FOREACH_DEVICE_TYPE_NODE_VARGS(device_type, fn, ...) \
DT_FOREACH_NODE_VARGS(DT_FOREACH_DEVICE_TYPE_NODE_VARGS_INTERNAL, device_type, fn, \
__VA_ARGS__)

/**
* @brief Invokes @p fn for every node in the tree with a ``zephyr,device-type`` property matching
* @p device_type.
*
* The macro @p fn must take one parameter, which will be a node identifier. The macro is expanded
* once for each node in the tree with a ``zephyr,device-type`` property matching @p device_type and
* with status `okay` (as usual, a missing status property is treated as status `okay`). The order
* that nodes are visited in is not specified.
*
* @param device_type lowercase-and-underscores device type
* @param fn macro to invoke
*/
#define DT_FOREACH_DEVICE_TYPE_STATUS_OKAY_NODE(device_type, fn) \
DT_FOREACH_STATUS_OKAY_NODE_VARGS(DT_FOREACH_DEVICE_TYPE_NODE_INTERNAL, device_type, fn)

/**
* @brief Invokes @p fn for every node in the tree with a ``zephyr,device-type`` property matching
* @p device_type with multiple arguments.
*
* The macro @p fn takes multiple arguments. The first should be the node identifier for the
* node. The macro is expanded once for each node in the tree with a ``zephyr,device-type`` property
* matching @p device_type and with status `okay` (as usual, a missing status property is treated as
* status `okay`). The order that nodes are visited in is not specified.
*
* @param device_type lowercase-and-underscores device type
* @param fn macro to invoke
*/
#define DT_FOREACH_DEVICE_TYPE_STATUS_OKAY_NODE_VARGS(device_type, fn, ...) \
DT_FOREACH_STATUS_OKAY_NODE_VARGS(DT_FOREACH_DEVICE_TYPE_NODE_VARGS_INTERNAL, device_type, \
fn, __VA_ARGS__)

/**
* @}
*/

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_INCLUDE_DEVICETREE_DEVICE_TYPE_H_ */
14 changes: 14 additions & 0 deletions scripts/dts/gen_defines.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,9 @@ def write_special_props(node):
write_fixed_partitions(node)
write_gpio_hogs(node)

# Macros that are special to Zephyr bindings.
write_zephyr_device_type(node)

def write_ranges(node):
# ranges property: edtlib knows the right #address-cells and
# #size-cells of parent and child, and can therefore pack the
Expand Down Expand Up @@ -618,6 +621,17 @@ def write_gpio_hogs(node):
for macro, val in macro2val.items():
out_dt_define(macro, val)

def write_zephyr_device_type(node):
# Write special macros for zephyr,device-type properties.

if 'zephyr,device-type' in node.props:
macro = f"{node.z_path_id}_ZEPHYR_DEVICE_TYPE"
device_type = node.props['zephyr,device-type'].val_as_token

out_comment("Zephyr device type:")
out_dt_define(f"{macro}_EXISTS", 1)
out_dt_define(f"{macro}_{device_type}", 1)

def write_vanilla_props(node):
# Writes macros for any and all properties defined in the
# "properties" section of the binding for the node.
Expand Down
19 changes: 19 additions & 0 deletions tests/lib/devicetree/api/app.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,25 @@
};
};
};

test_unique_device_type_reserved: test_unique_device_type_reserved {
compatible = "test-unique-device-type";
status = "reserved";
};

test_unique_device_type_disabled: test_unique_device_type_disabled {
compatible = "test-unique-device-type";
status = "disabled";
};

test_unique_device_type_no_status: test_unique_device_type_no_status {
compatible = "test-unique-device-type";
};

test_unique_device_type_okay: test_unique_device_type_okay {
compatible = "test-unique-device-type";
status = "okay";
};
};

test_64 {
Expand Down
13 changes: 13 additions & 0 deletions tests/lib/devicetree/api/dts/bindings/test-unique-device-type.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright (c) 2024 Vestas Wind Systems A/S
# SPDX-License-Identifier: Apache-2.0

description: Test node with unique zephyr,device-type

compatible: "test-unique-device-type"

include: base.yaml

properties:
zephyr,device-type:
default: test-unique-device-type-string
const: test-unique-device-type-string
Loading