1 /*
2 * Copyright 2024 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @file
9 * @brief Public APIs for the SCMI transport layer drivers
10 */
11
12 #ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_TRANSPORT_H_
13 #define _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_TRANSPORT_H_
14
15 #include <zephyr/device.h>
16 #include <zephyr/kernel.h>
17
18 struct scmi_message;
19 struct scmi_channel;
20
21 /**
22 * @typedef scmi_channel_cb
23 *
24 * @brief Callback function for message replies
25 *
26 * This function should be called by the transport layer
27 * driver whenever a reply to a previously sent message
28 * has been received. Its purpose is to notifying the SCMI
29 * core of the reply's arrival so that proper action can
30 * be taken.
31 *
32 * @param chan pointer to SCMI channel on which the reply
33 * arrived
34 */
35 typedef void (*scmi_channel_cb)(struct scmi_channel *chan);
36
37 /**
38 * @struct scmi_channel
39 * @brief SCMI channel structure
40 *
41 * An SCMI channel is a medium through which a protocol
42 * is able to transmit/receive messages. Each of the SCMI
43 * channels is represented by a `struct scmi_channel`.
44 */
45 struct scmi_channel {
46 /**
47 * channel lock. This is meant to be initialized
48 * and used only by the SCMI core to assure that
49 * only one protocol can send/receive messages
50 * through a channel at a given moment.
51 */
52 struct k_mutex lock;
53 /**
54 * binary semaphore. This is meant to be initialized
55 * and used only by the SCMI core. Its purpose is to
56 * signal that a reply has been received.
57 */
58 struct k_sem sem;
59 /** channel private data */
60 void *data;
61 /**
62 * callback function. This is meant to be set by
63 * the SCMI core and should be called by the SCMI
64 * transport layer driver whenever a reply has
65 * been received.
66 */
67 scmi_channel_cb cb;
68 /** is the channel ready to be used by a protocol? */
69 bool ready;
70 };
71
72 struct scmi_transport_api {
73 int (*init)(const struct device *transport);
74 int (*send_message)(const struct device *transport,
75 struct scmi_channel *chan,
76 struct scmi_message *msg);
77 int (*setup_chan)(const struct device *transport,
78 struct scmi_channel *chan,
79 bool tx);
80 int (*read_message)(const struct device *transport,
81 struct scmi_channel *chan,
82 struct scmi_message *msg);
83 bool (*channel_is_free)(const struct device *transport,
84 struct scmi_channel *chan);
85 struct scmi_channel *(*request_channel)(const struct device *transport,
86 uint32_t proto, bool tx);
87 };
88
89 /**
90 * @brief Request an SCMI channel dynamically
91 *
92 * Whenever the SCMI transport layer driver doesn't support
93 * static channel allocation, the SCMI core will try to bind
94 * a channel to a protocol dynamically using this function.
95 * Note that no setup needs to be performed on the channel
96 * in this function as the core will also call the channel
97 * setup() function.
98 *
99 * @param transport pointer to the device structure for the
100 * transport layer
101 * @param proto ID of the protocol for which the core is
102 * requesting the channel
103 * @param tx true if the channel is TX, false if RX
104 *
105 * @retval pointer to SCMI channel that's to be bound
106 * to the protocol
107 * @retval NULL if operation was not successful
108 */
109 static inline struct scmi_channel *
scmi_transport_request_channel(const struct device * transport,uint32_t proto,bool tx)110 scmi_transport_request_channel(const struct device *transport,
111 uint32_t proto, bool tx)
112 {
113 const struct scmi_transport_api *api =
114 (const struct scmi_transport_api *)transport->api;
115
116 if (api->request_channel) {
117 return api->request_channel(transport, proto, tx);
118 }
119
120 return NULL;
121 }
122
123 /**
124 * @brief Perform initialization for the transport layer driver
125 *
126 * The transport layer driver can't be initialized directly
127 * (i.e via a call to its init() function) during system initialization.
128 * This is because the macro used to define an SCMI transport places
129 * `scmi_core_transport_init()` in the init section instead of the
130 * driver's init() function. As such, `scmi_core_transport_init()`
131 * needs to call this function to perfrom transport layer driver
132 * initialization if required.
133 *
134 * This operation is optional.
135 *
136 * @param transport pointer to the device structure for the
137 * transport layer
138 *
139 * @retval 0 if successful
140 * @retval negative errno code if failure
141 */
scmi_transport_init(const struct device * transport)142 static inline int scmi_transport_init(const struct device *transport)
143 {
144 const struct scmi_transport_api *api =
145 (const struct scmi_transport_api *)transport->api;
146
147 if (api->init) {
148 return api->init(transport);
149 }
150
151 return 0;
152 }
153
154 /**
155 * @brief Setup an SCMI channel
156 *
157 * Before being able to send/receive messages, an SCMI channel needs
158 * to be prepared, which is what this function does. If it returns
159 * successfully, an SCMI protocol will be able to use this channel
160 * to send/receive messages.
161 *
162 * @param transport pointer to the device structure for the
163 * transport layer
164 * @param chan pointer to SCMI channel to be prepared
165 * @param tx true if the channel is TX, false if RX
166 *
167 * @retval 0 if successful
168 * @retval negative errno code if failure
169 */
scmi_transport_setup_chan(const struct device * transport,struct scmi_channel * chan,bool tx)170 static inline int scmi_transport_setup_chan(const struct device *transport,
171 struct scmi_channel *chan,
172 bool tx)
173 {
174 const struct scmi_transport_api *api =
175 (const struct scmi_transport_api *)transport->api;
176
177 if (!api || !api->setup_chan) {
178 return -ENOSYS;
179 }
180
181 return api->setup_chan(transport, chan, tx);
182 }
183
184 /**
185 * @brief Send an SCMI channel
186 *
187 * Send an SCMI message using given SCMI channel. This function is
188 * not allowed to block.
189 *
190 * @param transport pointer to the device structure for the
191 * transport layer
192 * @param chan pointer to SCMI channel on which the message
193 * is to be sent
194 * @param msg pointer to message the caller wishes to send
195 *
196 * @retval 0 if successful
197 * @retval negative errno code if failure
198 */
scmi_transport_send_message(const struct device * transport,struct scmi_channel * chan,struct scmi_message * msg)199 static inline int scmi_transport_send_message(const struct device *transport,
200 struct scmi_channel *chan,
201 struct scmi_message *msg)
202 {
203 const struct scmi_transport_api *api =
204 (const struct scmi_transport_api *)transport->api;
205
206 if (!api || !api->send_message) {
207 return -ENOSYS;
208 }
209
210 return api->send_message(transport, chan, msg);
211 }
212
213 /**
214 * @brief Read an SCMI message
215 *
216 * @param transport pointer to the device structure for the
217 * transport layer
218 * @param chan pointer to SCMI channel on which the message
219 * is to be read
220 * @param msg pointer to message the caller wishes to read
221 *
222 * @retval 0 if successful
223 * @retval negative errno code if failure
224 */
scmi_transport_read_message(const struct device * transport,struct scmi_channel * chan,struct scmi_message * msg)225 static inline int scmi_transport_read_message(const struct device *transport,
226 struct scmi_channel *chan,
227 struct scmi_message *msg)
228 {
229 const struct scmi_transport_api *api =
230 (const struct scmi_transport_api *)transport->api;
231
232 if (!api || !api->read_message) {
233 return -ENOSYS;
234 }
235
236 return api->read_message(transport, chan, msg);
237 }
238
239 /**
240 * @brief Check if an SCMI channel is free
241 *
242 * @param transport pointer to the device structure for
243 * the transport layer
244 * @param chan pointer to SCMI channel the query is to be
245 * performed on
246 *
247 * @retval 0 if successful
248 * @retval negative errno code if failure
249 */
scmi_transport_channel_is_free(const struct device * transport,struct scmi_channel * chan)250 static inline bool scmi_transport_channel_is_free(const struct device *transport,
251 struct scmi_channel *chan)
252 {
253 const struct scmi_transport_api *api =
254 (const struct scmi_transport_api *)transport->api;
255
256 if (!api || !api->channel_is_free) {
257 return -ENOSYS;
258 }
259
260 return api->channel_is_free(transport, chan);
261 }
262
263 /**
264 * @brief Perfrom SCMI core initialization
265 *
266 * @param transport pointer to the device structure for
267 * the transport layer
268 *
269 * @retval 0 if successful
270 * @retval negative errno code if failure
271 */
272 int scmi_core_transport_init(const struct device *transport);
273
274 #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_TRANSPORT_H_ */
275