1 /**
2  * @file
3  * @brief Port / Endpoint Devicetree macro public API header file.
4  */
5 
6 /*
7  * Copyright 2024 NXP
8  * Copyright (c) 2024 tinyVision.ai Inc
9  *
10  * SPDX-License-Identifier: Apache-2.0
11  */
12 
13 #ifndef ZEPHYR_INCLUDE_DEVICETREE_PORT_ENDPOINT_H_
14 #define ZEPHYR_INCLUDE_DEVICETREE_PORT_ENDPOINT_H_
15 
16 #ifdef __cplusplus
17 extern "C" {
18 #endif
19 
20 /**
21  * @defgroup devicetree-port-endpoint Devicetree Port Endpoint API
22  * @ingroup devicetree
23  * @{
24  */
25 
26 /**
27  * @brief Helper for @ref DT_INST_PORT_BY_ID
28  *
29  * This behaves the same way as @ref DT_INST_PORT_BY_ID but does not work if there is only
30  * a single port without address.
31  *
32  * @param inst instance number
33  * @param pid port ID
34  * @return port node associated with @p pid
35  */
36 #define _DT_INST_PORT_BY_ID(inst, pid)                                                             \
37 	COND_CODE_1(DT_NODE_EXISTS(DT_INST_CHILD(inst, ports)),                                    \
38 	    (DT_CHILD(DT_INST_CHILD(inst, ports), port_##pid)), (DT_INST_CHILD(inst, port_##pid)))
39 
40 /**
41  * @brief Get a port node from its id
42  *
43  * Given a device instance number, return a port node specified by its ID.
44  * It handles various ways of how a port could be defined.
45  *
46  * Example usage with DT_INST_PORT_BY_ID() to get the @c port@0 or @c port node:
47  *
48  * @code{.c}
49  *	DT_INST_PORT_BY_ID(inst, 0)
50  * @endcode
51  *
52  * Example devicetree fragment:
53  *
54  * @code{.dts}
55  *	&device {
56  *		ports {
57  *			#address-cells = <1>;
58  *			#size-cells = <0>;
59  *			port@0 {
60  *				reg = <0x0>;
61  *			};
62  *		};
63  *	};
64  * @endcode
65  *
66  * @code{.dts}
67  *	&device {
68  *		#address-cells = <1>;
69  *		#size-cells = <0>;
70  *		port@0 {
71  *			reg = <0x0>;
72  *		};
73  *	};
74  * @endcode
75  *
76  * @code{.dts}
77  *	&device {
78  *		port {
79  *		};
80  *	};
81  * @endcode
82  *
83  * @param inst instance number
84  * @param pid port ID
85  * @return port node associated with @p pid
86  */
87 #define DT_INST_PORT_BY_ID(inst, pid)                                                              \
88 	COND_CODE_1(DT_NODE_EXISTS(_DT_INST_PORT_BY_ID(inst, pid)),                                \
89 		(_DT_INST_PORT_BY_ID(inst, pid)), (DT_INST_CHILD(inst, port)))
90 
91 /**
92  * @brief Helper for @ref DT_INST_ENDPOINT_BY_ID
93  *
94  * This behaves the same way as @ref DT_INST_PORT_BY_ID but does not work if there is only
95  * a single endpoint without address.
96  *
97  * @param inst instance number
98  * @param pid port ID
99  * @param eid endpoint ID
100  * @return endpoint node associated with @p eid and @p pid
101  */
102 #define _DT_INST_ENDPOINT_BY_ID(inst, pid, eid)                                                    \
103 	DT_CHILD(DT_INST_PORT_BY_ID(inst, pid), endpoint_##eid)
104 
105 /**
106  * @brief Get an endpoint node from its id and its parent port id
107  *
108  * Given a device instance number, a port ID and an endpoint ID, return the endpoint node.
109  * It handles various ways of how a port and an endpoint could be defined as described in
110  * @ref DT_INST_PORT_BY_ID and below.
111  *
112  * Example usage with DT_INST_ENDPOINT_BY_ID() to get the @c endpoint or @c endpoint@0 node:
113  *
114  * @code{.c}
115  *	DT_INST_ENDPOINT_BY_ID(inst, 0, 0)
116  * @endcode
117  *
118  * Example devicetree fragment:
119  *
120  * @code{.dts}
121  *	&device {
122  *		port {
123  *			endpoint {
124  *			};
125  *		};
126  *	};
127  * @endcode
128  *
129  * @code{.dts}
130  *	&device {
131  *		port {
132  *			#address-cells = <1>;
133  *			#size-cells = <0>;
134  *			endpoint@0 {
135  *				reg = <0x0>;
136  *			};
137  *		};
138  *	};
139  * @endcode
140  *
141  * @code{.dts}
142  *	&device {
143  *		ports {
144  *			#address-cells = <1>;
145  *			#size-cells = <0>;
146  *			port@0 {
147  *				reg = <0x0>;
148  *				#address-cells = <1>;
149  *				#size-cells = <0>;
150  *				endpoint@0 {
151  *					reg = <0x0>;
152  *				};
153  *			};
154  *		};
155  *	};
156  * @endcode
157  *
158  * @param inst instance number
159  * @param pid port ID
160  * @param eid endpoint ID
161  * @return endpoint node associated with @p eid and @p pid
162  */
163 #define DT_INST_ENDPOINT_BY_ID(inst, pid, eid)                                                     \
164 	COND_CODE_1(DT_NODE_EXISTS(_DT_INST_ENDPOINT_BY_ID(inst, pid, eid)),                       \
165 		(_DT_INST_ENDPOINT_BY_ID(inst, pid, eid)),                                         \
166 			(DT_CHILD(DT_INST_PORT_BY_ID(inst, pid), endpoint)))
167 
168 /**
169  * @brief Get the device node from its endpoint node.
170  *
171  * Given an endpoint node id, return its device node id.
172  * This handles various ways of how a port and an endpoint could be defined as described in
173  * @ref DT_NODE_BY_ENDPOINT.
174  *
175  * Example usage with DT_NODE_BY_ENDPOINT() to get the @c &device node from its @c ep0 node:
176  *
177  * @code{.c}
178  *	DT_NODE_BY_ENDPOINT(DT_NODELABEL(ep0))
179  * @endcode
180  *
181  * Example devicetree fragment:
182  *
183  * @code{.dts}
184  *	&device {
185  *		port {
186  *			#address-cells = <1>;
187  *			#size-cells = <0>;
188  *			ep0: endpoint@0 {
189  *				reg = <0x0>;
190  *			};
191  *		};
192  *	};
193  * @endcode
194  *
195  * @code{.dts}
196  *	&device {
197  *		ports {
198  *			#address-cells = <1>;
199  *			#size-cells = <0>;
200  *			port@0 {
201  *				reg = <0x0>;
202  *				#address-cells = <1>;
203  *				#size-cells = <0>;
204  *				ep0: endpoint@0 {
205  *					reg = <0x0>;
206  *				};
207  *			};
208  *		};
209  *	};
210  * @endcode
211  *
212  * @param ep endpoint node
213  * @return device node associated with @p ep
214  */
215 #define DT_NODE_BY_ENDPOINT(ep)                                                                    \
216 	COND_CODE_1(DT_NODE_EXISTS(DT_CHILD(DT_PARENT(DT_GPARENT(ep)), ports)),                    \
217 		(DT_PARENT(DT_GPARENT(ep))), (DT_GPARENT(ep)))
218 
219 /**
220  * @brief Get the remote device node from a local endpoint node.
221  *
222  * Given an endpoint node id, return the remote device node that connects to this device via this
223  * local endpoint. This handles various ways of how a port and an endpoint could be defined as
224  * described in @ref DT_INST_PORT_BY_ID and @ref DT_INST_ENDPOINT_BY_ID.
225  *
226  * Example usage with DT_NODE_REMOTE_DEVICE() to get the remote device node @c &device1 from the
227  * local endpoint @c endpoint@0 node of the device @c &device0 node:
228  *
229  * @code{.c}
230  *	DT_NODE_REMOTE_DEVICE(DT_NODELABEL(device0_ep_out))
231  * @endcode
232  *
233  * Example devicetree fragment:
234  *
235  * @code{.dts}
236  *	&device0 {
237  *		port {
238  *			#address-cells = <1>;
239  *			#size-cells = <0>;
240  *			device0_ep_out: endpoint@0 {
241  *				reg = <0x0>;
242  *				remote-endpoint-label = "device1_ep_in";
243  *			};
244  *		};
245  *	};
246  *
247  *	&device1 {
248  *		ports {
249  *			#address-cells = <1>;
250  *			#size-cells = <0>;
251  *			port@0 {
252  *				reg = <0x0>;
253  *				device1_ep_in: endpoint {
254  *					remote-endpoint-label = "device0_ep_out";
255  *				};
256  *			};
257  *		};
258  *	};
259  * @endcode
260  *
261  * @param ep endpoint node
262  * @return remote device node that connects to this device via @p ep
263  */
264 #define DT_NODE_REMOTE_DEVICE(ep)                                                                \
265 	DT_NODE_BY_ENDPOINT(DT_NODELABEL(DT_STRING_TOKEN(ep, remote_endpoint_label)))
266 
267 /**
268  * @}
269  */
270 
271 #ifdef __cplusplus
272 }
273 #endif
274 
275 #endif /* ZEPHYR_INCLUDE_DEVICETREE_PORT_ENDPOINT_H_ */
276