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