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