1 /*
2  * Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #ifndef ZEPHYR_INCLUDE_DRIVERS_MBOX_H_
7 #define ZEPHYR_INCLUDE_DRIVERS_MBOX_H_
8 
9 #include <errno.h>
10 #include <stdint.h>
11 #include <stdlib.h>
12 
13 #include <zephyr/device.h>
14 #include <zephyr/devicetree.h>
15 
16 #ifdef __cplusplus
17 extern "C" {
18 #endif
19 
20 /**
21  * @brief MBOX Interface
22  * @defgroup mbox_interface MBOX Interface
23  * @since 1.0
24  * @version 0.1.0
25  * @ingroup io_interfaces
26  * @{
27  *
28  * @code{.unparsed}
29  *
30  *                      CPU #1        |
31  * +----------+                       |        +----------+
32  * |          +---TX9----+   +--------+--RX8---+          |
33  * |  dev A   |          |   |        |        |  CPU #2  |
34  * |          <---RX8--+ |   | +------+--TX9--->          |
35  * +----------+        | |   | |      |        +----------+
36  *                  +--+-v---v-+--+   |
37  *                  |             |   |
38  *                  |   MBOX dev  |   |
39  *                  |             |   |
40  *                  +--+-^---^--+-+   |
41  * +----------+        | |   |  |     |        +----------+
42  * |          <---RX2--+ |   |  +-----+--TX3--->          |
43  * |  dev B   |          |   |        |        |  CPU #3  |
44  * |          +---TX3----+   +--------+--RX2---+          |
45  * +----------+                       |        +----------+
46  *                                    |
47  *
48  * @endcode
49  *
50  * An MBOX device is a peripheral capable of passing signals (and data depending
51  * on the peripheral) between CPUs and clusters in the system. Each MBOX
52  * instance is providing one or more channels, each one targeting one other CPU
53  * cluster (multiple channels can target the same cluster).
54  *
55  * For example in the plot the device 'dev A' is using the TX channel 9 to
56  * signal (or send data to) the CPU #2 and it's expecting data or signals on
57  * the RX channel 8. Thus it can send the message through the channel 9, and it
58  * can register a callback on the channel 8 of the MBOX device.
59  *
60  * This API supports two modes: signalling mode and data transfer mode.
61  *
62  * In signalling mode:
63  *  - mbox_mtu_get() must return 0
64  *  - mbox_send() must have (msg == NULL)
65  *  - the callback must be called with (data == NULL)
66  *
67  * In data transfer mode:
68  *  - mbox_mtu_get() must return a (value != 0)
69  *  - mbox_send() must have (msg != NULL)
70  *  - the callback must be called with (data != NULL)
71  *  - The msg content must be the same between sender and receiver
72  *
73  */
74 
75 /** @brief Type for MBOX channel identifiers */
76 typedef uint32_t mbox_channel_id_t;
77 
78 /** @brief Message struct (to hold data and its size). */
79 struct mbox_msg {
80 	/** Pointer to the data sent in the message. */
81 	const void *data;
82 	/** Size of the data. */
83 	size_t size;
84 };
85 
86 /** @brief MBOX specification from DT */
87 struct mbox_dt_spec {
88 	/** MBOX device pointer. */
89 	const struct device *dev;
90 	/** Channel ID. */
91 	mbox_channel_id_t channel_id;
92 };
93 
94 /**
95  * @brief Structure initializer for struct mbox_dt_spec from devicetree
96  *
97  * This helper macro expands to a static initializer for a struct mbox_dt_spec
98  * by reading the relevant device controller and channel number from the
99  * devicetree.
100  *
101  * Example devicetree fragment:
102  *
103  * @code{.devicetree}
104  *     n: node {
105  *             mboxes = <&mbox1 8>,
106  *                      <&mbox1 9>;
107  *             mbox-names = "tx", "rx";
108  *     };
109  * @endcode
110  *
111  * Example usage:
112  *
113  * @code{.c}
114  *     const struct mbox_dt_spec spec = MBOX_DT_SPEC_GET(DT_NODELABEL(n), tx);
115  * @endcode
116  *
117  * @param node_id Devicetree node identifier for the MBOX device
118  * @param name lowercase-and-underscores name of the mboxes element
119  *
120  * @return static initializer for a struct mbox_dt_spec
121  */
122 #define MBOX_DT_SPEC_GET(node_id, name)                                        \
123 	{                                                                      \
124 		.dev = DEVICE_DT_GET(DT_MBOX_CTLR_BY_NAME(node_id, name)),     \
125 		.channel_id = DT_MBOX_CHANNEL_BY_NAME(node_id, name),          \
126 	}
127 
128 /**
129  * @brief Instance version of MBOX_DT_CHANNEL_GET()
130  *
131  * @param inst DT_DRV_COMPAT instance number
132  * @param name lowercase-and-underscores name of the mboxes element
133  *
134  * @return static initializer for a struct mbox_dt_spec
135  */
136 #define MBOX_DT_SPEC_INST_GET(inst, name)                                      \
137 	MBOX_DT_SPEC_GET(DT_DRV_INST(inst), name)
138 
139 /** @cond INTERNAL_HIDDEN */
140 
141 /**
142  * @brief Callback API for incoming MBOX messages
143  *
144  * These callbacks execute in interrupt context. Therefore, use only
145  * interrupt-safe APIs. Registration of callbacks is done via
146  * mbox_register_callback()
147  *
148  * The data parameter must be NULL in signalling mode.
149  *
150  * @param dev MBOX device instance
151  * @param channel_id Channel ID
152  * @param user_data Pointer to some private data provided at registration time
153  * @param data Message struct
154  */
155 typedef void (*mbox_callback_t)(const struct device *dev,
156 				mbox_channel_id_t channel_id, void *user_data,
157 				struct mbox_msg *data);
158 
159 /**
160  * @brief Callback API to send MBOX messages
161  *
162  * @param dev MBOX device instance
163  * @param channel_id Channel ID
164  * @param msg Message struct
165  *
166  * @return See the return values for mbox_send()
167  * @see mbox_send()
168  */
169 typedef int (*mbox_send_t)(const struct device *dev,
170 			   mbox_channel_id_t channel_id,
171 			   const struct mbox_msg *msg);
172 
173 /**
174  * @brief Callback API to get maximum data size
175  *
176  * @param dev MBOX device instance
177  *
178  * @return See the return values for mbox_mtu_get()
179  * @see mbox_mtu_get()
180  */
181 typedef int (*mbox_mtu_get_t)(const struct device *dev);
182 
183 /**
184  * @brief Callback API upon registration
185  *
186  * @param dev MBOX device instance
187  * @param channel_id Channel ID
188  * @param cb Callback function to execute on incoming message interrupts.
189  * @param user_data Application-specific data pointer which will be passed to
190  *                  the callback function when executed.
191  *
192  * @return See return values for mbox_register_callback()
193  * @see mbox_register_callback()
194  */
195 typedef int (*mbox_register_callback_t)(const struct device *dev,
196 					mbox_channel_id_t channel_id,
197 					mbox_callback_t cb, void *user_data);
198 
199 /**
200  * @brief Callback API upon enablement of interrupts
201  *
202  * @param dev MBOX device instance
203  * @param channel_id Channel ID
204  * @param enables Set to 0 to disable and to nonzero to enable.
205  *
206  * @return See return values for mbox_set_enabled()
207  * @see mbox_set_enabled()
208  */
209 typedef int (*mbox_set_enabled_t)(const struct device *dev,
210 				  mbox_channel_id_t channel_id, bool enabled);
211 
212 /**
213  * @brief Callback API to get maximum number of channels
214  *
215  * @param dev MBOX device instance
216  *
217  * @return See return values for mbox_max_channels_get()
218  * @see mbox_max_channels_get()
219  */
220 typedef uint32_t (*mbox_max_channels_get_t)(const struct device *dev);
221 
222 __subsystem struct mbox_driver_api {
223 	mbox_send_t send;
224 	mbox_register_callback_t register_callback;
225 	mbox_mtu_get_t mtu_get;
226 	mbox_max_channels_get_t max_channels_get;
227 	mbox_set_enabled_t set_enabled;
228 };
229 
230 /** @endcond */
231 
232 /**
233  * @brief Validate if MBOX device instance from a struct mbox_dt_spec is ready.
234  *
235  * @param spec MBOX specification from devicetree
236  *
237  * @return See return values for mbox_send()
238  */
mbox_is_ready_dt(const struct mbox_dt_spec * spec)239 static inline bool mbox_is_ready_dt(const struct mbox_dt_spec *spec)
240 {
241 	return device_is_ready(spec->dev);
242 }
243 
244 /**
245  * @brief Try to send a message over the MBOX device.
246  *
247  * Send a message over an struct mbox_channel. The msg parameter must be NULL
248  * when the driver is used for signalling.
249  *
250  * If the msg parameter is not NULL, this data is expected to be delivered on
251  * the receiving side using the data parameter of the receiving callback.
252  *
253  * @param dev MBOX device instance
254  * @param channel_id MBOX channel identifier
255  * @param msg Message
256  *
257  * @retval 0         On success.
258  * @retval -EBUSY    If the remote hasn't yet read the last data sent.
259  * @retval -EMSGSIZE If the supplied data size is unsupported by the driver.
260  * @retval -EINVAL   If there was a bad parameter, such as: too-large channel
261  *		     descriptor or the device isn't an outbound MBOX channel.
262  */
263 __syscall int mbox_send(const struct device *dev, mbox_channel_id_t channel_id,
264 			const struct mbox_msg *msg);
265 
z_impl_mbox_send(const struct device * dev,mbox_channel_id_t channel_id,const struct mbox_msg * msg)266 static inline int z_impl_mbox_send(const struct device *dev,
267 				   mbox_channel_id_t channel_id,
268 				   const struct mbox_msg *msg)
269 {
270 	const struct mbox_driver_api *api =
271 		(const struct mbox_driver_api *)dev->api;
272 
273 	if (api->send == NULL) {
274 		return -ENOSYS;
275 	}
276 
277 	return api->send(dev, channel_id, msg);
278 }
279 
280 /**
281  * @brief Try to send a message over the MBOX device from a struct mbox_dt_spec.
282  *
283  * @param spec MBOX specification from devicetree
284  * @param msg Message
285  *
286  * @return See return values for mbox_send()
287  */
mbox_send_dt(const struct mbox_dt_spec * spec,const struct mbox_msg * msg)288 static inline int mbox_send_dt(const struct mbox_dt_spec *spec,
289 			       const struct mbox_msg *msg)
290 {
291 	return mbox_send(spec->dev, spec->channel_id, msg);
292 }
293 
294 /**
295  * @brief Register a callback function on a channel for incoming messages.
296  *
297  * This function doesn't assume anything concerning the status of the
298  * interrupts. Use mbox_set_enabled() to enable or to disable the interrupts
299  * if needed.
300  *
301  * @param dev MBOX device instance
302  * @param channel_id MBOX channel identifier
303  * @param cb Callback function to execute on incoming message interrupts.
304  * @param user_data Application-specific data pointer which will be passed
305  *                  to the callback function when executed.
306  *
307  * @retval 0      On success.
308  * @retval -errno Negative errno on error.
309  */
mbox_register_callback(const struct device * dev,mbox_channel_id_t channel_id,mbox_callback_t cb,void * user_data)310 static inline int mbox_register_callback(const struct device *dev,
311 					 mbox_channel_id_t channel_id,
312 					 mbox_callback_t cb,
313 					 void *user_data)
314 {
315 	const struct mbox_driver_api *api =
316 		(const struct mbox_driver_api *)dev->api;
317 
318 	if (api->register_callback == NULL) {
319 		return -ENOSYS;
320 	}
321 
322 	return api->register_callback(dev, channel_id, cb, user_data);
323 }
324 
325 /**
326  * @brief Register a callback function on a channel for incoming messages from a
327  *        struct mbox_dt_spec.
328  *
329  * @param spec MBOX specification from devicetree
330  * @param cb Callback function to execute on incoming message interrupts.
331  * @param user_data Application-specific data pointer which will be passed
332  *                  to the callback function when executed.
333  *
334  * @return See return values for mbox_register_callback()
335  */
mbox_register_callback_dt(const struct mbox_dt_spec * spec,mbox_callback_t cb,void * user_data)336 static inline int mbox_register_callback_dt(const struct mbox_dt_spec *spec,
337 					    mbox_callback_t cb, void *user_data)
338 {
339 	return mbox_register_callback(spec->dev, spec->channel_id, cb,
340 				      user_data);
341 }
342 
343 /**
344  * @brief Return the maximum number of bytes possible in an outbound message.
345  *
346  * Returns the actual number of bytes that it is possible to send through an
347  * outgoing channel.
348  *
349  * This number can be 0 when the driver only supports signalling or when on the
350  * receiving side the content and size of the message must be retrieved in an
351  * indirect way (i.e. probing some other peripheral, reading memory regions,
352  * etc...).
353  *
354  * If this function returns 0, the msg parameter in mbox_send() is expected
355  * to be NULL.
356  *
357  * @param dev MBOX device instance.
358  *
359  * @retval >0     Maximum possible size of a message in bytes
360  * @retval 0      Indicates signalling
361  * @retval -errno Negative errno on error.
362  */
363 __syscall int mbox_mtu_get(const struct device *dev);
364 
z_impl_mbox_mtu_get(const struct device * dev)365 static inline int z_impl_mbox_mtu_get(const struct device *dev)
366 {
367 	const struct mbox_driver_api *api =
368 		(const struct mbox_driver_api *)dev->api;
369 
370 	if (api->mtu_get == NULL) {
371 		return -ENOSYS;
372 	}
373 
374 	return api->mtu_get(dev);
375 }
376 
377 /**
378  * @brief Return the maximum number of bytes possible in an outbound message
379  *        from struct mbox_dt_spec.
380  *
381  * @param spec MBOX specification from devicetree
382  *
383  * @return See return values for mbox_register_callback()
384  */
mbox_mtu_get_dt(const struct mbox_dt_spec * spec)385 static inline int mbox_mtu_get_dt(const struct mbox_dt_spec *spec)
386 {
387 	return mbox_mtu_get(spec->dev);
388 }
389 
390 /**
391  * @brief Enable (disable) interrupts and callbacks for inbound channels.
392  *
393  * Enable interrupt for the channel when the parameter 'enable' is set to true.
394  * Disable it otherwise.
395  *
396  * Immediately after calling this function with 'enable' set to true, the
397  * channel is considered enabled and ready to receive signal and messages (even
398  * already pending), so the user must take care of installing a proper callback
399  * (if needed) using mbox_register_callback() on the channel before enabling
400  * it. For this reason it is recommended that all the channels are disabled at
401  * probe time.
402  *
403  * Enabling a channel for which there is no installed callback is considered
404  * undefined behavior (in general the driver must take care of gracefully
405  * handling spurious interrupts with no installed callback).
406  *
407  * @param dev MBOX device instance
408  * @param channel_id MBOX channel identifier
409  * @param enabled Enable (true) or disable (false) the channel.
410  *
411  * @retval 0         On success.
412  * @retval -EINVAL   If it isn't an inbound channel.
413  * @retval -EALREADY If channel is already @p enabled.
414  */
415 __syscall int mbox_set_enabled(const struct device *dev,
416 			       mbox_channel_id_t channel_id, bool enabled);
417 
z_impl_mbox_set_enabled(const struct device * dev,mbox_channel_id_t channel_id,bool enabled)418 static inline int z_impl_mbox_set_enabled(const struct device *dev,
419 					  mbox_channel_id_t channel_id,
420 					  bool enabled)
421 {
422 	const struct mbox_driver_api *api =
423 		(const struct mbox_driver_api *)dev->api;
424 
425 	if (api->set_enabled == NULL) {
426 		return -ENOSYS;
427 	}
428 
429 	return api->set_enabled(dev, channel_id, enabled);
430 }
431 
432 /**
433  * @brief Enable (disable) interrupts and callbacks for inbound channels from a
434  *        struct mbox_dt_spec.
435  *
436  * @param spec MBOX specification from devicetree
437  * @param enabled Enable (true) or disable (false) the channel.
438  *
439  * @return See return values for mbox_set_enabled()
440  */
mbox_set_enabled_dt(const struct mbox_dt_spec * spec,bool enabled)441 static inline int mbox_set_enabled_dt(const struct mbox_dt_spec *spec,
442 				      bool enabled)
443 {
444 	return mbox_set_enabled(spec->dev, spec->channel_id, enabled);
445 }
446 
447 /**
448  * @brief Return the maximum number of channels.
449  *
450  * Return the maximum number of channels supported by the hardware.
451  *
452  * @param dev MBOX device instance.
453  *
454  * @return >0     Maximum possible number of supported channels on success
455  * @return -errno Negative errno on error.
456  */
457 __syscall uint32_t mbox_max_channels_get(const struct device *dev);
458 
z_impl_mbox_max_channels_get(const struct device * dev)459 static inline uint32_t z_impl_mbox_max_channels_get(const struct device *dev)
460 {
461 	const struct mbox_driver_api *api =
462 		(const struct mbox_driver_api *)dev->api;
463 
464 	if (api->max_channels_get == NULL) {
465 		return -ENOSYS;
466 	}
467 
468 	return api->max_channels_get(dev);
469 }
470 
471 /**
472  * @brief Return the maximum number of channels from a struct mbox_dt_spec.
473  *
474  * @param spec MBOX specification from devicetree
475  *
476  * @return See return values for mbox_max_channels_get()
477  */
mbox_max_channels_get_dt(const struct mbox_dt_spec * spec)478 static inline int mbox_max_channels_get_dt(const struct mbox_dt_spec *spec)
479 {
480 	return mbox_max_channels_get(spec->dev);
481 }
482 
483 /** @} */
484 
485 #ifdef __cplusplus
486 }
487 #endif
488 
489 #include <zephyr/syscalls/mbox.h>
490 
491 #endif /* ZEPHYR_INCLUDE_DRIVERS_MBOX_H_ */
492