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