1 /**
2  * @file
3  *
4  * @brief Generic low-level inter-processor mailbox communication API.
5  */
6 
7 /*
8  * Copyright (c) 2015 Intel Corporation
9  *
10  * SPDX-License-Identifier: Apache-2.0
11  */
12 
13 #ifndef ZEPHYR_INCLUDE_DRIVERS_IPM_H_
14 #define ZEPHYR_INCLUDE_DRIVERS_IPM_H_
15 
16 /**
17  * @brief IPM Interface
18  * @defgroup ipm_interface IPM Interface
19  * @since 1.0
20  * @version 1.0.0
21  * @ingroup io_interfaces
22  * @{
23  */
24 
25 #include <zephyr/kernel.h>
26 #include <zephyr/device.h>
27 
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31 
32 /**
33  * @typedef ipm_callback_t
34  * @brief Callback API for incoming IPM messages
35  *
36  * These callbacks execute in interrupt context. Therefore, use only
37  * interrupt-safe APIS. Registration of callbacks is done via
38  * @a ipm_register_callback
39  *
40  * @param ipmdev Driver instance
41  * @param user_data Pointer to some private data provided at registration
42  *        time.
43  * @param id Message type identifier.
44  * @param data Message data pointer. The correct amount of data to read out
45  *        must be inferred using the message id/upper level protocol.
46  */
47 typedef void (*ipm_callback_t)(const struct device *ipmdev, void *user_data,
48 			       uint32_t id, volatile void *data);
49 
50 /**
51  * @typedef ipm_send_t
52  * @brief Callback API to send IPM messages
53  *
54  * See @a ipm_send() for argument definitions.
55  */
56 typedef int (*ipm_send_t)(const struct device *ipmdev, int wait, uint32_t id,
57 			  const void *data, int size);
58 /**
59  * @typedef ipm_max_data_size_get_t
60  * @brief Callback API to get maximum data size
61  *
62  * See @a ipm_max_data_size_get() for argument definitions.
63  */
64 typedef int (*ipm_max_data_size_get_t)(const struct device *ipmdev);
65 
66 /**
67  * @typedef ipm_max_id_val_get_t
68  * @brief Callback API to get the ID's maximum value
69  *
70  * See @a ipm_max_id_val_get() for argument definitions.
71  */
72 typedef uint32_t (*ipm_max_id_val_get_t)(const struct device *ipmdev);
73 
74 /**
75  * @typedef ipm_register_callback_t
76  * @brief Callback API upon registration
77  *
78  * See @a ipm_register_callback() for argument definitions.
79  */
80 typedef void (*ipm_register_callback_t)(const struct device *port,
81 					ipm_callback_t cb,
82 					void *user_data);
83 
84 /**
85  * @typedef ipm_set_enabled_t
86  * @brief Callback API upon enablement of interrupts
87  *
88  * See @a ipm_set_enabled() for argument definitions.
89  */
90 typedef int (*ipm_set_enabled_t)(const struct device *ipmdev, int enable);
91 
92 /**
93  * @typedef ipm_complete_t
94  * @brief Callback API upon command completion
95  *
96  * See @a ipm_complete() for argument definitions.
97  */
98 typedef void (*ipm_complete_t)(const struct device *ipmdev);
99 
100 __subsystem struct ipm_driver_api {
101 	ipm_send_t send;
102 	ipm_register_callback_t register_callback;
103 	ipm_max_data_size_get_t max_data_size_get;
104 	ipm_max_id_val_get_t max_id_val_get;
105 	ipm_set_enabled_t set_enabled;
106 #ifdef CONFIG_IPM_CALLBACK_ASYNC
107 	ipm_complete_t complete;
108 #endif
109 };
110 
111 /**
112  * @brief Try to send a message over the IPM device.
113  *
114  * A message is considered consumed once the remote interrupt handler
115  * finishes. If there is deferred processing on the remote side,
116  * or if outgoing messages must be queued and wait on an
117  * event/semaphore, a high-level driver can implement that.
118  *
119  * There are constraints on how much data can be sent or the maximum value
120  * of id. Use the @a ipm_max_data_size_get and @a ipm_max_id_val_get routines
121  * to determine them.
122  *
123  * The @a size parameter is used only on the sending side to determine
124  * the amount of data to put in the message registers. It is not passed along
125  * to the receiving side. The upper-level protocol dictates the amount of
126  * data read back.
127  *
128  * @param ipmdev Driver instance
129  * @param wait If nonzero, busy-wait for remote to consume the message. The
130  *	       message is considered consumed once the remote interrupt handler
131  *	       finishes. If there is deferred processing on the remote side,
132  *	       or you would like to queue outgoing messages and wait on an
133  *	       event/semaphore, you can implement that in a high-level driver
134  * @param id Message identifier. Values are constrained by
135  *        @a ipm_max_data_size_get since many boards only allow for a
136  *        subset of bits in a 32-bit register to store the ID.
137  * @param data Pointer to the data sent in the message.
138  * @param size Size of the data.
139  *
140  * @retval -EBUSY    If the remote hasn't yet read the last data sent.
141  * @retval -EMSGSIZE If the supplied data size is unsupported by the driver.
142  * @retval -EINVAL   If there was a bad parameter, such as: too-large id value.
143  *                   or the device isn't an outbound IPM channel.
144  * @retval 0         On success.
145  */
146 __syscall int ipm_send(const struct device *ipmdev, int wait, uint32_t id,
147 		       const void *data, int size);
148 
z_impl_ipm_send(const struct device * ipmdev,int wait,uint32_t id,const void * data,int size)149 static inline int z_impl_ipm_send(const struct device *ipmdev, int wait,
150 				  uint32_t id,
151 				  const void *data, int size)
152 {
153 	const struct ipm_driver_api *api =
154 		(const struct ipm_driver_api *)ipmdev->api;
155 
156 	return api->send(ipmdev, wait, id, data, size);
157 }
158 
159 /**
160  * @brief Register a callback function for incoming messages.
161  *
162  * @param ipmdev Driver instance pointer.
163  * @param cb Callback function to execute on incoming message interrupts.
164  * @param user_data Application-specific data pointer which will be passed
165  *        to the callback function when executed.
166  */
ipm_register_callback(const struct device * ipmdev,ipm_callback_t cb,void * user_data)167 static inline void ipm_register_callback(const struct device *ipmdev,
168 					 ipm_callback_t cb, void *user_data)
169 {
170 	const struct ipm_driver_api *api =
171 		(const struct ipm_driver_api *)ipmdev->api;
172 
173 	api->register_callback(ipmdev, cb, user_data);
174 }
175 
176 /**
177  * @brief Return the maximum number of bytes possible in an outbound message.
178  *
179  * IPM implementations vary on the amount of data that can be sent in a
180  * single message since the data payload is typically stored in registers.
181  *
182  * @param ipmdev Driver instance pointer.
183  *
184  * @return Maximum possible size of a message in bytes.
185  */
186 __syscall int ipm_max_data_size_get(const struct device *ipmdev);
187 
z_impl_ipm_max_data_size_get(const struct device * ipmdev)188 static inline int z_impl_ipm_max_data_size_get(const struct device *ipmdev)
189 {
190 	const struct ipm_driver_api *api =
191 		(const struct ipm_driver_api *)ipmdev->api;
192 
193 	return api->max_data_size_get(ipmdev);
194 }
195 
196 
197 /**
198  * @brief Return the maximum id value possible in an outbound message.
199  *
200  * Many IPM implementations store the message's ID in a register with
201  * some bits reserved for other uses.
202  *
203  * @param ipmdev Driver instance pointer.
204  *
205  * @return Maximum possible value of a message ID.
206  */
207 __syscall uint32_t ipm_max_id_val_get(const struct device *ipmdev);
208 
z_impl_ipm_max_id_val_get(const struct device * ipmdev)209 static inline uint32_t z_impl_ipm_max_id_val_get(const struct device *ipmdev)
210 {
211 	const struct ipm_driver_api *api =
212 		(const struct ipm_driver_api *)ipmdev->api;
213 
214 	return api->max_id_val_get(ipmdev);
215 }
216 
217 /**
218  * @brief Enable interrupts and callbacks for inbound channels.
219  *
220  * @param ipmdev Driver instance pointer.
221  * @param enable Set to 0 to disable and to nonzero to enable.
222  *
223  * @retval 0       On success.
224  * @retval -EINVAL If it isn't an inbound channel.
225  */
226 __syscall int ipm_set_enabled(const struct device *ipmdev, int enable);
227 
z_impl_ipm_set_enabled(const struct device * ipmdev,int enable)228 static inline int z_impl_ipm_set_enabled(const struct device *ipmdev,
229 					 int enable)
230 {
231 	const struct ipm_driver_api *api =
232 		(const struct ipm_driver_api *)ipmdev->api;
233 
234 	return api->set_enabled(ipmdev, enable);
235 }
236 
237 /**
238  * @brief Signal asynchronous command completion
239  *
240  * Some IPM backends have an ability to deliver a command
241  * asynchronously.  The callback will be invoked in interrupt context,
242  * but the message (including the provided data pointer) will stay
243  * "active" and unacknowledged until later code (presumably in thread
244  * mode) calls ipm_complete().
245  *
246  * This function is, obviously, a noop on drivers without async
247  * support.
248  *
249  * @param ipmdev Driver instance pointer.
250  */
251 __syscall void ipm_complete(const struct device *ipmdev);
252 
z_impl_ipm_complete(const struct device * ipmdev)253 static inline void z_impl_ipm_complete(const struct device *ipmdev)
254 {
255 #ifdef CONFIG_IPM_CALLBACK_ASYNC
256 	const struct ipm_driver_api *api =
257 		(const struct ipm_driver_api *)ipmdev->api;
258 
259 	if (api->complete != NULL) {
260 		api->complete(ipmdev);
261 	}
262 #endif
263 }
264 
265 #ifdef __cplusplus
266 }
267 #endif
268 
269 /**
270  * @}
271  */
272 
273 #include <zephyr/syscalls/ipm.h>
274 
275 #endif /* ZEPHYR_INCLUDE_DRIVERS_IPM_H_ */
276