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