Line data Source code
1 0 : /*
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 1 : typedef uint32_t mbox_channel_id_t;
77 :
78 : /** @brief Message struct (to hold data and its size). */
79 1 : struct mbox_msg {
80 : /** Pointer to the data sent in the message. */
81 1 : const void *data;
82 : /** Size of the data. */
83 1 : size_t size;
84 : };
85 :
86 : /** @brief MBOX specification from DT */
87 1 : struct mbox_dt_spec {
88 : /** MBOX device pointer. */
89 1 : const struct device *dev;
90 : /** Channel ID. */
91 1 : 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 1 : #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 1 : #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 enabled 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 : */
239 1 : 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 1 : __syscall int mbox_send(const struct device *dev, mbox_channel_id_t channel_id,
264 : const struct mbox_msg *msg);
265 :
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 : */
288 1 : 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 : */
310 1 : 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 : */
336 1 : 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 1 : __syscall int mbox_mtu_get(const struct device *dev);
364 :
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 : */
385 1 : 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 1 : __syscall int mbox_set_enabled(const struct device *dev,
416 : mbox_channel_id_t channel_id, bool enabled);
417 :
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 : */
441 1 : 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 1 : __syscall uint32_t mbox_max_channels_get(const struct device *dev);
458 :
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 : */
478 1 : 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_ */
|