1 /*
2  * Copyright (c) 2024, Ambiq Micro Inc. <www.ambiq.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifndef ZEPHYR_INCLUDE_DRIVERS_MSPI_DEVICETREE_H_
8 #define ZEPHYR_INCLUDE_DRIVERS_MSPI_DEVICETREE_H_
9 
10 /**
11  * @brief MSPI Devicetree related macros
12  * @defgroup mspi_devicetree MSPI Devicetree related macros
13  * @ingroup io_interfaces
14  * @{
15  */
16 
17 #include <zephyr/drivers/gpio.h>
18 
19 #ifdef __cplusplus
20 extern "C" {
21 #endif
22 
23 /**
24  * @brief Structure initializer for <tt>struct mspi_dev_cfg</tt> from devicetree
25  *
26  * This helper macro expands to a static initializer for a <tt>struct
27  * mspi_dev_cfg</tt> by reading the relevant data from the devicetree.
28  *
29  * @param mspi_dev Devicetree node identifier for the MSPI device whose
30  *                 struct mspi_dev_cfg to create an initializer for
31  */
32 #define MSPI_DEVICE_CONFIG_DT(mspi_dev)                                                           \
33 	{                                                                                         \
34 		.ce_num               = DT_PROP_OR(mspi_dev, mspi_hardware_ce_num, 0),            \
35 		.freq                 = DT_PROP(mspi_dev, mspi_max_frequency),                    \
36 		.io_mode              = DT_ENUM_IDX_OR(mspi_dev, mspi_io_mode,                    \
37 						MSPI_IO_MODE_SINGLE),                             \
38 		.data_rate            = DT_ENUM_IDX_OR(mspi_dev, mspi_data_rate,                  \
39 						MSPI_DATA_RATE_SINGLE),                           \
40 		.cpp                  = DT_ENUM_IDX_OR(mspi_dev, mspi_cpp_mode, MSPI_CPP_MODE_0), \
41 		.endian               = DT_ENUM_IDX_OR(mspi_dev, mspi_endian,                     \
42 						MSPI_XFER_LITTLE_ENDIAN),                         \
43 		.ce_polarity          = DT_ENUM_IDX_OR(mspi_dev, mspi_ce_polarity,                \
44 						MSPI_CE_ACTIVE_LOW),                              \
45 		.dqs_enable           = DT_PROP(mspi_dev, mspi_dqs_enable),                       \
46 		.rx_dummy             = DT_PROP_OR(mspi_dev, rx_dummy, 0),                        \
47 		.tx_dummy             = DT_PROP_OR(mspi_dev, tx_dummy, 0),                        \
48 		.read_cmd             = DT_PROP_OR(mspi_dev, read_command, 0),                    \
49 		.write_cmd            = DT_PROP_OR(mspi_dev, write_command, 0),                   \
50 		.cmd_length           = DT_ENUM_IDX_OR(mspi_dev, command_length, 0),              \
51 		.addr_length          = DT_ENUM_IDX_OR(mspi_dev, address_length, 0),              \
52 		.mem_boundary         = COND_CODE_1(DT_NODE_HAS_PROP(mspi_dev, ce_break_config),  \
53 						(DT_PROP_BY_IDX(mspi_dev, ce_break_config, 0)),   \
54 						(0)),                                             \
55 		.time_to_break        = COND_CODE_1(DT_NODE_HAS_PROP(mspi_dev, ce_break_config),  \
56 						(DT_PROP_BY_IDX(mspi_dev, ce_break_config, 1)),   \
57 						(0)),                                             \
58 	}
59 
60 /**
61  * @brief Structure initializer for <tt>struct mspi_dev_cfg</tt> from devicetree instance
62  *
63  * This is equivalent to
64  * <tt>MSPI_DEVICE_CONFIG_DT(DT_DRV_INST(inst))</tt>.
65  *
66  * @param inst Devicetree instance number
67  */
68 #define MSPI_DEVICE_CONFIG_DT_INST(inst) MSPI_DEVICE_CONFIG_DT(DT_DRV_INST(inst))
69 
70 /**
71  * @brief Structure initializer for <tt>struct mspi_xip_cfg</tt> from devicetree
72  *
73  * This helper macro expands to a static initializer for a <tt>struct
74  * mspi_xip_cfg</tt> by reading the relevant data from the devicetree.
75  *
76  * @param mspi_dev Devicetree node identifier for the MSPI device whose
77  *                 struct mspi_xip_cfg to create an initializer for
78  */
79 #define MSPI_XIP_CONFIG_DT_NO_CHECK(mspi_dev)                                                     \
80 	{                                                                                         \
81 		.enable               = DT_PROP_BY_IDX(mspi_dev, xip_config, 0),                  \
82 		.address_offset       = DT_PROP_BY_IDX(mspi_dev, xip_config, 1),                  \
83 		.size                 = DT_PROP_BY_IDX(mspi_dev, xip_config, 2),                  \
84 		.permission           = DT_PROP_BY_IDX(mspi_dev, xip_config, 3),                  \
85 	}
86 
87 /**
88  * @brief Structure initializer for <tt>struct mspi_xip_cfg</tt> from devicetree
89  *
90  * This helper macro check whether <tt>xip_config</tt> binding exist first
91  * before calling <tt>MSPI_XIP_CONFIG_DT_NO_CHECK</tt>.
92  *
93  * @param mspi_dev Devicetree node identifier for the MSPI device whose
94  *                 struct mspi_xip_cfg to create an initializer for
95  */
96 #define MSPI_XIP_CONFIG_DT(mspi_dev)                                                              \
97 		COND_CODE_1(DT_NODE_HAS_PROP(mspi_dev, xip_config),                               \
98 			(MSPI_XIP_CONFIG_DT_NO_CHECK(mspi_dev)),                                  \
99 			({}))
100 
101 /**
102  * @brief Structure initializer for <tt>struct mspi_xip_cfg</tt> from devicetree instance
103  *
104  * This is equivalent to
105  * <tt>MSPI_XIP_CONFIG_DT(DT_DRV_INST(inst))</tt>.
106  *
107  * @param inst Devicetree instance number
108  */
109 #define MSPI_XIP_CONFIG_DT_INST(inst) MSPI_XIP_CONFIG_DT(DT_DRV_INST(inst))
110 
111 /**
112  * @brief Structure initializer for <tt>struct mspi_scramble_cfg</tt> from devicetree
113  *
114  * This helper macro expands to a static initializer for a <tt>struct
115  * mspi_scramble_cfg</tt> by reading the relevant data from the devicetree.
116  *
117  * @param mspi_dev Devicetree node identifier for the MSPI device whose
118  *                 struct mspi_scramble_cfg to create an initializer for
119  */
120 #define MSPI_SCRAMBLE_CONFIG_DT_NO_CHECK(mspi_dev)                                                \
121 	{                                                                                         \
122 		.enable               = DT_PROP_BY_IDX(mspi_dev, scramble_config, 0),             \
123 		.address_offset       = DT_PROP_BY_IDX(mspi_dev, scramble_config, 1),             \
124 		.size                 = DT_PROP_BY_IDX(mspi_dev, scramble_config, 2),             \
125 	}
126 
127 /**
128  * @brief Structure initializer for <tt>struct mspi_scramble_cfg</tt> from devicetree
129  *
130  * This helper macro check whether <tt>scramble_config</tt> binding exist first
131  * before calling <tt>MSPI_SCRAMBLE_CONFIG_DT_NO_CHECK</tt>.
132  *
133  * @param mspi_dev Devicetree node identifier for the MSPI device whose
134  *                 struct mspi_scramble_cfg to create an initializer for
135  */
136 #define MSPI_SCRAMBLE_CONFIG_DT(mspi_dev)                                                         \
137 		COND_CODE_1(DT_NODE_HAS_PROP(mspi_dev, scramble_config),                          \
138 			(MSPI_SCRAMBLE_CONFIG_DT_NO_CHECK(mspi_dev)),                             \
139 			({}))
140 
141 /**
142  * @brief Structure initializer for <tt>struct mspi_scramble_cfg</tt> from devicetree instance
143  *
144  * This is equivalent to
145  * <tt>MSPI_SCRAMBLE_CONFIG_DT(DT_DRV_INST(inst))</tt>.
146  *
147  * @param inst Devicetree instance number
148  */
149 #define MSPI_SCRAMBLE_CONFIG_DT_INST(inst) MSPI_SCRAMBLE_CONFIG_DT(DT_DRV_INST(inst))
150 
151 /**
152  * @brief Structure initializer for <tt>struct mspi_dev_id</tt> from devicetree
153  *
154  * This helper macro expands to a static initializer for a <tt>struct
155  * mspi_dev_id</tt> by reading the relevant data from the devicetree.
156  *
157  * @param mspi_dev Devicetree node identifier for the MSPI device whose
158  *                 struct mspi_dev_id to create an initializer for
159  */
160 #define MSPI_DEVICE_ID_DT(mspi_dev)                                                               \
161 	{                                                                                         \
162 		.ce                   = MSPI_DEV_CE_GPIOS_DT_SPEC_GET(mspi_dev),                  \
163 		.dev_idx              = DT_REG_ADDR(mspi_dev),                                    \
164 	}
165 
166 /**
167  * @brief Structure initializer for <tt>struct mspi_dev_id</tt> from devicetree instance
168  *
169  * This is equivalent to
170  * <tt>MSPI_DEVICE_ID_DT(DT_DRV_INST(inst))</tt>.
171  *
172  * @param inst Devicetree instance number
173  */
174 #define MSPI_DEVICE_ID_DT_INST(inst) MSPI_DEVICE_ID_DT(DT_DRV_INST(inst))
175 
176 
177 /**
178  * @brief Get a <tt>struct gpio_dt_spec</tt> for a MSPI device's chip enable pin
179  *
180  * Example devicetree fragment:
181  *
182  * @code{.devicetree}
183  *     gpio1: gpio@abcd0001 { ... };
184  *
185  *     gpio2: gpio@abcd0002 { ... };
186  *
187  *     mspi@abcd0003 {
188  *             compatible = "ambiq,mspi";
189  *             ce-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>,
190  *                        <&gpio2 20 GPIO_ACTIVE_LOW>;
191  *
192  *             a: mspi-dev-a@0 {
193  *                     reg = <0>;
194  *             };
195  *
196  *             b: mspi-dev-b@1 {
197  *                     reg = <1>;
198  *             };
199  *     };
200  * @endcode
201  *
202  * Example usage:
203  *
204  * @code{.c}
205  *     MSPI_DEV_CE_GPIOS_DT_SPEC_GET(DT_NODELABEL(a)) \
206  *           // { DEVICE_DT_GET(DT_NODELABEL(gpio1)), 10, GPIO_ACTIVE_LOW }
207  *     MSPI_DEV_CE_GPIOS_DT_SPEC_GET(DT_NODELABEL(b)) \
208  *           // { DEVICE_DT_GET(DT_NODELABEL(gpio2)), 20, GPIO_ACTIVE_LOW }
209  * @endcode
210  *
211  * @param mspi_dev a MSPI device node identifier
212  * @return #gpio_dt_spec struct corresponding with mspi_dev's chip enable
213  */
214 #define MSPI_DEV_CE_GPIOS_DT_SPEC_GET(mspi_dev)                                                   \
215 		GPIO_DT_SPEC_GET_BY_IDX_OR(DT_BUS(mspi_dev), ce_gpios,                            \
216 					   DT_REG_ADDR_RAW(mspi_dev), {})
217 
218 /**
219  * @brief Get a <tt>struct gpio_dt_spec</tt> for a MSPI device's chip enable pin
220  *
221  * This is equivalent to
222  * <tt>MSPI_DEV_CE_GPIOS_DT_SPEC_GET(DT_DRV_INST(inst))</tt>.
223  *
224  * @param inst Devicetree instance number
225  * @return #gpio_dt_spec struct corresponding with mspi_dev's chip enable
226  */
227 #define MSPI_DEV_CE_GPIOS_DT_SPEC_INST_GET(inst)                                                  \
228 		MSPI_DEV_CE_GPIOS_DT_SPEC_GET(DT_DRV_INST(inst))
229 
230 
231 /**
232  * @brief Get an array of <tt>struct gpio_dt_spec</tt> from devicetree for a MSPI controller
233  *
234  * This helper macro check whether <tt>ce_gpios</tt> binding exist first
235  * before calling <tt>GPIO_DT_SPEC_GET_BY_IDX</tt> and expand to an array of
236  * gpio_dt_spec.
237  *
238  * @param node_id Devicetree node identifier for the MSPI controller
239  * @return an array of gpio_dt_spec struct corresponding with mspi_dev's chip enables
240  */
241 #define MSPI_CE_GPIOS_DT_SPEC_GET(node_id)                                                        \
242 {                                                                                                 \
243 	COND_CODE_1(DT_NODE_HAS_PROP(node_id, ce_gpios),                                          \
244 		(DT_FOREACH_PROP_ELEM_SEP(node_id, ce_gpios, GPIO_DT_SPEC_GET_BY_IDX, (,))),      \
245 		())                                                                               \
246 }
247 
248 /**
249  * @brief Get an array of <tt>struct gpio_dt_spec</tt> for a MSPI controller
250  *
251  * This is equivalent to
252  * <tt>MSPI_CE_GPIOS_DT_SPEC_GET(DT_DRV_INST(inst))</tt>.
253  *
254  * @param inst Devicetree instance number
255  * @return an array of gpio_dt_spec struct corresponding with mspi_dev's chip enables
256  */
257 #define MSPI_CE_GPIOS_DT_SPEC_INST_GET(inst)                                                      \
258 		MSPI_CE_GPIOS_DT_SPEC_GET(DT_DRV_INST(inst))
259 
260 /**
261  * @brief Initialize and get a pointer to a @p mspi_ce_control from a
262  *        devicetree node identifier
263  *
264  * This helper is useful for initializing a device on a MSPI bus. It
265  * initializes a struct mspi_ce_control and returns a pointer to it.
266  * Here, @p node_id is a node identifier for a MSPI device, not a MSPI
267  * controller.
268  *
269  * Example devicetree fragment:
270  *
271  * @code{.devicetree}
272  *     mspi@abcd0001 {
273  *             ce-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
274  *             mspidev: mspi-device@0 { ... };
275  *     };
276  * @endcode
277  *
278  * Example usage:
279  *
280  * @code{.c}
281  *     struct mspi_ce_control ctrl =
282  *             MSPI_CE_CONTROL_INIT(DT_NODELABEL(mspidev), 2);
283  * @endcode
284  *
285  * This example is equivalent to:
286  *
287  * @code{.c}
288  *     struct mspi_ce_control ctrl = {
289  *             .gpio = MSPI_DEV_CE_GPIOS_DT_SPEC_GET(DT_NODELABEL(mspidev)),
290  *             .delay = 2,
291  *     };
292  * @endcode
293  *
294  * @param node_id Devicetree node identifier for a device on a MSPI bus
295  * @param delay_ The @p delay field to set in the @p mspi_ce_control
296  * @return a pointer to the @p mspi_ce_control structure
297  */
298 #define MSPI_CE_CONTROL_INIT(node_id, delay_)                                                     \
299 	{                                                                                         \
300 		.gpio = MSPI_DEV_CE_GPIOS_DT_SPEC_GET(node_id), .delay = (delay_),                \
301 	}
302 
303 /**
304  * @brief Get a pointer to a @p mspi_ce_control from a devicetree node
305  *
306  * This is equivalent to
307  * <tt>MSPI_CE_CONTROL_INIT(DT_DRV_INST(inst), delay)</tt>.
308  *
309  * Therefore, @p DT_DRV_COMPAT must already be defined before using
310  * this macro.
311  *
312  * @param inst Devicetree node instance number
313  * @param delay_ The @p delay field to set in the @p mspi_ce_control
314  * @return a pointer to the @p mspi_ce_control structure
315  */
316 #define MSPI_CE_CONTROL_INIT_INST(inst, delay_) MSPI_CE_CONTROL_INIT(DT_DRV_INST(inst), delay_)
317 
318 #ifdef __cplusplus
319 }
320 #endif
321 
322 /**
323  * @}
324  */
325 
326 #endif /* ZEPHYR_INCLUDE_DRIVERS_MSPI_DEVICETREE_H_ */
327