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