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