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