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