Commit | Line | Data |
---|---|---|
5f90f189 EC |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2019-2021 Linaro Ltd. | |
4 | */ | |
5 | ||
6 | #include <linux/io.h> | |
7 | #include <linux/of.h> | |
8 | #include <linux/of_address.h> | |
9 | #include <linux/kernel.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/mutex.h> | |
12 | #include <linux/slab.h> | |
13 | #include <linux/tee_drv.h> | |
14 | #include <linux/uuid.h> | |
15 | #include <uapi/linux/tee.h> | |
16 | ||
17 | #include "common.h" | |
18 | ||
19 | #define SCMI_OPTEE_MAX_MSG_SIZE 128 | |
20 | ||
21 | enum scmi_optee_pta_cmd { | |
22 | /* | |
23 | * PTA_SCMI_CMD_CAPABILITIES - Get channel capabilities | |
24 | * | |
25 | * [out] value[0].a: Capability bit mask (enum pta_scmi_caps) | |
26 | * [out] value[0].b: Extended capabilities or 0 | |
27 | */ | |
28 | PTA_SCMI_CMD_CAPABILITIES = 0, | |
29 | ||
30 | /* | |
31 | * PTA_SCMI_CMD_PROCESS_SMT_CHANNEL - Process SCMI message in SMT buffer | |
32 | * | |
33 | * [in] value[0].a: Channel handle | |
34 | * | |
35 | * Shared memory used for SCMI message/response exhange is expected | |
36 | * already identified and bound to channel handle in both SCMI agent | |
37 | * and SCMI server (OP-TEE) parts. | |
38 | * The memory uses SMT header to carry SCMI meta-data (protocol ID and | |
39 | * protocol message ID). | |
40 | */ | |
41 | PTA_SCMI_CMD_PROCESS_SMT_CHANNEL = 1, | |
42 | ||
43 | /* | |
44 | * PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE - Process SMT/SCMI message | |
45 | * | |
46 | * [in] value[0].a: Channel handle | |
47 | * [in/out] memref[1]: Message/response buffer (SMT and SCMI payload) | |
48 | * | |
49 | * Shared memory used for SCMI message/response is a SMT buffer | |
50 | * referenced by param[1]. It shall be 128 bytes large to fit response | |
51 | * payload whatever message playload size. | |
52 | * The memory uses SMT header to carry SCMI meta-data (protocol ID and | |
53 | * protocol message ID). | |
54 | */ | |
55 | PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE = 2, | |
56 | ||
57 | /* | |
58 | * PTA_SCMI_CMD_GET_CHANNEL - Get channel handle | |
59 | * | |
60 | * SCMI shm information are 0 if agent expects to use OP-TEE regular SHM | |
61 | * | |
62 | * [in] value[0].a: Channel identifier | |
63 | * [out] value[0].a: Returned channel handle | |
64 | * [in] value[0].b: Requested capabilities mask (enum pta_scmi_caps) | |
65 | */ | |
66 | PTA_SCMI_CMD_GET_CHANNEL = 3, | |
5e114ad9 EC |
67 | |
68 | /* | |
69 | * PTA_SCMI_CMD_PROCESS_MSG_CHANNEL - Process SCMI message in a MSG | |
70 | * buffer pointed by memref parameters | |
71 | * | |
72 | * [in] value[0].a: Channel handle | |
73 | * [in] memref[1]: Message buffer (MSG and SCMI payload) | |
74 | * [out] memref[2]: Response buffer (MSG and SCMI payload) | |
75 | * | |
76 | * Shared memories used for SCMI message/response are MSG buffers | |
77 | * referenced by param[1] and param[2]. MSG transport protocol | |
78 | * uses a 32bit header to carry SCMI meta-data (protocol ID and | |
79 | * protocol message ID) followed by the effective SCMI message | |
80 | * payload. | |
81 | */ | |
82 | PTA_SCMI_CMD_PROCESS_MSG_CHANNEL = 4, | |
5f90f189 EC |
83 | }; |
84 | ||
85 | /* | |
86 | * OP-TEE SCMI service capabilities bit flags (32bit) | |
87 | * | |
88 | * PTA_SCMI_CAPS_SMT_HEADER | |
89 | * When set, OP-TEE supports command using SMT header protocol (SCMI shmem) in | |
90 | * shared memory buffers to carry SCMI protocol synchronisation information. | |
5e114ad9 EC |
91 | * |
92 | * PTA_SCMI_CAPS_MSG_HEADER | |
93 | * When set, OP-TEE supports command using MSG header protocol in an OP-TEE | |
94 | * shared memory to carry SCMI protocol synchronisation information and SCMI | |
95 | * message payload. | |
5f90f189 EC |
96 | */ |
97 | #define PTA_SCMI_CAPS_NONE 0 | |
98 | #define PTA_SCMI_CAPS_SMT_HEADER BIT(0) | |
5e114ad9 EC |
99 | #define PTA_SCMI_CAPS_MSG_HEADER BIT(1) |
100 | #define PTA_SCMI_CAPS_MASK (PTA_SCMI_CAPS_SMT_HEADER | \ | |
101 | PTA_SCMI_CAPS_MSG_HEADER) | |
5f90f189 EC |
102 | |
103 | /** | |
104 | * struct scmi_optee_channel - Description of an OP-TEE SCMI channel | |
105 | * | |
106 | * @channel_id: OP-TEE channel ID used for this transport | |
107 | * @tee_session: TEE session identifier | |
108 | * @caps: OP-TEE SCMI channel capabilities | |
109 | * @mu: Mutex protection on channel access | |
110 | * @cinfo: SCMI channel information | |
111 | * @shmem: Virtual base address of the shared memory | |
5e114ad9 EC |
112 | * @req: Shared memory protocol handle for SCMI request and synchronous response |
113 | * @tee_shm: TEE shared memory handle @req or NULL if using IOMEM shmem | |
5f90f189 EC |
114 | * @link: Reference in agent's channel list |
115 | */ | |
116 | struct scmi_optee_channel { | |
117 | u32 channel_id; | |
118 | u32 tee_session; | |
119 | u32 caps; | |
75c8f430 | 120 | u32 rx_len; |
5f90f189 EC |
121 | struct mutex mu; |
122 | struct scmi_chan_info *cinfo; | |
5e114ad9 EC |
123 | union { |
124 | struct scmi_shared_mem __iomem *shmem; | |
125 | struct scmi_msg_payld *msg; | |
126 | } req; | |
5f90f189 EC |
127 | struct tee_shm *tee_shm; |
128 | struct list_head link; | |
129 | }; | |
130 | ||
131 | /** | |
132 | * struct scmi_optee_agent - OP-TEE transport private data | |
133 | * | |
134 | * @dev: Device used for communication with TEE | |
135 | * @tee_ctx: TEE context used for communication | |
136 | * @caps: Supported channel capabilities | |
137 | * @mu: Mutex for protection of @channel_list | |
138 | * @channel_list: List of all created channels for the agent | |
139 | */ | |
140 | struct scmi_optee_agent { | |
141 | struct device *dev; | |
142 | struct tee_context *tee_ctx; | |
143 | u32 caps; | |
144 | struct mutex mu; | |
145 | struct list_head channel_list; | |
146 | }; | |
147 | ||
148 | /* There can be only 1 SCMI service in OP-TEE we connect to */ | |
149 | static struct scmi_optee_agent *scmi_optee_private; | |
150 | ||
151 | /* Forward reference to scmi_optee transport initialization */ | |
152 | static int scmi_optee_init(void); | |
153 | ||
154 | /* Open a session toward SCMI OP-TEE service with REE_KERNEL identity */ | |
155 | static int open_session(struct scmi_optee_agent *agent, u32 *tee_session) | |
156 | { | |
157 | struct device *dev = agent->dev; | |
158 | struct tee_client_device *scmi_pta = to_tee_client_device(dev); | |
159 | struct tee_ioctl_open_session_arg arg = { }; | |
160 | int ret; | |
161 | ||
162 | memcpy(arg.uuid, scmi_pta->id.uuid.b, TEE_IOCTL_UUID_LEN); | |
163 | arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; | |
164 | ||
165 | ret = tee_client_open_session(agent->tee_ctx, &arg, NULL); | |
166 | if (ret < 0 || arg.ret) { | |
167 | dev_err(dev, "Can't open tee session: %d / %#x\n", ret, arg.ret); | |
168 | return -EOPNOTSUPP; | |
169 | } | |
170 | ||
171 | *tee_session = arg.session; | |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
176 | static void close_session(struct scmi_optee_agent *agent, u32 tee_session) | |
177 | { | |
178 | tee_client_close_session(agent->tee_ctx, tee_session); | |
179 | } | |
180 | ||
181 | static int get_capabilities(struct scmi_optee_agent *agent) | |
182 | { | |
183 | struct tee_ioctl_invoke_arg arg = { }; | |
184 | struct tee_param param[1] = { }; | |
185 | u32 caps; | |
186 | u32 tee_session; | |
187 | int ret; | |
188 | ||
189 | ret = open_session(agent, &tee_session); | |
190 | if (ret) | |
191 | return ret; | |
192 | ||
193 | arg.func = PTA_SCMI_CMD_CAPABILITIES; | |
194 | arg.session = tee_session; | |
195 | arg.num_params = 1; | |
196 | ||
197 | param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; | |
198 | ||
199 | ret = tee_client_invoke_func(agent->tee_ctx, &arg, param); | |
200 | ||
201 | close_session(agent, tee_session); | |
202 | ||
203 | if (ret < 0 || arg.ret) { | |
204 | dev_err(agent->dev, "Can't get capabilities: %d / %#x\n", ret, arg.ret); | |
205 | return -EOPNOTSUPP; | |
206 | } | |
207 | ||
208 | caps = param[0].u.value.a; | |
209 | ||
5e114ad9 EC |
210 | if (!(caps & (PTA_SCMI_CAPS_SMT_HEADER | PTA_SCMI_CAPS_MSG_HEADER))) { |
211 | dev_err(agent->dev, "OP-TEE SCMI PTA doesn't support SMT and MSG\n"); | |
5f90f189 EC |
212 | return -EOPNOTSUPP; |
213 | } | |
214 | ||
215 | agent->caps = caps; | |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
220 | static int get_channel(struct scmi_optee_channel *channel) | |
221 | { | |
222 | struct device *dev = scmi_optee_private->dev; | |
223 | struct tee_ioctl_invoke_arg arg = { }; | |
224 | struct tee_param param[1] = { }; | |
5e114ad9 | 225 | unsigned int caps = 0; |
5f90f189 EC |
226 | int ret; |
227 | ||
5e114ad9 EC |
228 | if (channel->tee_shm) |
229 | caps = PTA_SCMI_CAPS_MSG_HEADER; | |
230 | else | |
231 | caps = PTA_SCMI_CAPS_SMT_HEADER; | |
232 | ||
5f90f189 EC |
233 | arg.func = PTA_SCMI_CMD_GET_CHANNEL; |
234 | arg.session = channel->tee_session; | |
235 | arg.num_params = 1; | |
236 | ||
237 | param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT; | |
238 | param[0].u.value.a = channel->channel_id; | |
239 | param[0].u.value.b = caps; | |
240 | ||
241 | ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param); | |
242 | ||
243 | if (ret || arg.ret) { | |
244 | dev_err(dev, "Can't get channel with caps %#x: %d / %#x\n", caps, ret, arg.ret); | |
245 | return -EOPNOTSUPP; | |
246 | } | |
247 | ||
248 | /* From now on use channel identifer provided by OP-TEE SCMI service */ | |
249 | channel->channel_id = param[0].u.value.a; | |
250 | channel->caps = caps; | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | static int invoke_process_smt_channel(struct scmi_optee_channel *channel) | |
256 | { | |
5e114ad9 EC |
257 | struct tee_ioctl_invoke_arg arg = { |
258 | .func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL, | |
259 | .session = channel->tee_session, | |
260 | .num_params = 1, | |
261 | }; | |
262 | struct tee_param param[1] = { }; | |
5f90f189 EC |
263 | int ret; |
264 | ||
5f90f189 EC |
265 | param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; |
266 | param[0].u.value.a = channel->channel_id; | |
267 | ||
5e114ad9 EC |
268 | ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param); |
269 | if (ret < 0 || arg.ret) { | |
270 | dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n", | |
271 | channel->channel_id, ret, arg.ret); | |
272 | return -EIO; | |
5f90f189 EC |
273 | } |
274 | ||
5e114ad9 EC |
275 | return 0; |
276 | } | |
277 | ||
278 | static int invoke_process_msg_channel(struct scmi_optee_channel *channel, size_t msg_size) | |
279 | { | |
280 | struct tee_ioctl_invoke_arg arg = { | |
281 | .func = PTA_SCMI_CMD_PROCESS_MSG_CHANNEL, | |
282 | .session = channel->tee_session, | |
283 | .num_params = 3, | |
284 | }; | |
285 | struct tee_param param[3] = { }; | |
286 | int ret; | |
287 | ||
288 | param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; | |
289 | param[0].u.value.a = channel->channel_id; | |
290 | ||
291 | param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; | |
292 | param[1].u.memref.shm = channel->tee_shm; | |
293 | param[1].u.memref.size = msg_size; | |
294 | ||
295 | param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; | |
296 | param[2].u.memref.shm = channel->tee_shm; | |
297 | param[2].u.memref.size = SCMI_OPTEE_MAX_MSG_SIZE; | |
298 | ||
5f90f189 EC |
299 | ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param); |
300 | if (ret < 0 || arg.ret) { | |
301 | dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n", | |
302 | channel->channel_id, ret, arg.ret); | |
303 | return -EIO; | |
304 | } | |
305 | ||
75c8f430 VG |
306 | /* Save response size */ |
307 | channel->rx_len = param[2].u.memref.size; | |
308 | ||
5f90f189 EC |
309 | return 0; |
310 | } | |
311 | ||
312 | static int scmi_optee_link_supplier(struct device *dev) | |
313 | { | |
314 | if (!scmi_optee_private) { | |
315 | if (scmi_optee_init()) | |
316 | dev_dbg(dev, "Optee bus not yet ready\n"); | |
317 | ||
318 | /* Wait for optee bus */ | |
319 | return -EPROBE_DEFER; | |
320 | } | |
321 | ||
322 | if (!device_link_add(dev, scmi_optee_private->dev, DL_FLAG_AUTOREMOVE_CONSUMER)) { | |
323 | dev_err(dev, "Adding link to supplier optee device failed\n"); | |
324 | return -ECANCELED; | |
325 | } | |
326 | ||
327 | return 0; | |
328 | } | |
329 | ||
330 | static bool scmi_optee_chan_available(struct device *dev, int idx) | |
331 | { | |
332 | u32 channel_id; | |
333 | ||
334 | return !of_property_read_u32_index(dev->of_node, "linaro,optee-channel-id", | |
335 | idx, &channel_id); | |
336 | } | |
337 | ||
338 | static void scmi_optee_clear_channel(struct scmi_chan_info *cinfo) | |
339 | { | |
340 | struct scmi_optee_channel *channel = cinfo->transport_info; | |
341 | ||
5e114ad9 EC |
342 | if (!channel->tee_shm) |
343 | shmem_clear_channel(channel->req.shmem); | |
344 | } | |
345 | ||
346 | static int setup_dynamic_shmem(struct device *dev, struct scmi_optee_channel *channel) | |
347 | { | |
348 | const size_t msg_size = SCMI_OPTEE_MAX_MSG_SIZE; | |
349 | void *shbuf; | |
350 | ||
351 | channel->tee_shm = tee_shm_alloc_kernel_buf(scmi_optee_private->tee_ctx, msg_size); | |
352 | if (IS_ERR(channel->tee_shm)) { | |
353 | dev_err(channel->cinfo->dev, "shmem allocation failed\n"); | |
354 | return -ENOMEM; | |
355 | } | |
356 | ||
357 | shbuf = tee_shm_get_va(channel->tee_shm, 0); | |
358 | memset(shbuf, 0, msg_size); | |
359 | channel->req.msg = shbuf; | |
75c8f430 | 360 | channel->rx_len = msg_size; |
5e114ad9 EC |
361 | |
362 | return 0; | |
5f90f189 EC |
363 | } |
364 | ||
5f90f189 EC |
365 | static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo, |
366 | struct scmi_optee_channel *channel) | |
367 | { | |
368 | struct device_node *np; | |
369 | resource_size_t size; | |
370 | struct resource res; | |
371 | int ret; | |
372 | ||
373 | np = of_parse_phandle(cinfo->dev->of_node, "shmem", 0); | |
374 | if (!of_device_is_compatible(np, "arm,scmi-shmem")) { | |
375 | ret = -ENXIO; | |
376 | goto out; | |
377 | } | |
378 | ||
379 | ret = of_address_to_resource(np, 0, &res); | |
380 | if (ret) { | |
381 | dev_err(dev, "Failed to get SCMI Tx shared memory\n"); | |
382 | goto out; | |
383 | } | |
384 | ||
385 | size = resource_size(&res); | |
386 | ||
5e114ad9 EC |
387 | channel->req.shmem = devm_ioremap(dev, res.start, size); |
388 | if (!channel->req.shmem) { | |
5f90f189 EC |
389 | dev_err(dev, "Failed to ioremap SCMI Tx shared memory\n"); |
390 | ret = -EADDRNOTAVAIL; | |
391 | goto out; | |
392 | } | |
393 | ||
394 | ret = 0; | |
395 | ||
396 | out: | |
397 | of_node_put(np); | |
398 | ||
399 | return ret; | |
400 | } | |
401 | ||
402 | static int setup_shmem(struct device *dev, struct scmi_chan_info *cinfo, | |
403 | struct scmi_optee_channel *channel) | |
404 | { | |
405 | if (of_find_property(cinfo->dev->of_node, "shmem", NULL)) | |
406 | return setup_static_shmem(dev, cinfo, channel); | |
407 | else | |
5e114ad9 | 408 | return setup_dynamic_shmem(dev, channel); |
5f90f189 EC |
409 | } |
410 | ||
411 | static int scmi_optee_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, bool tx) | |
412 | { | |
413 | struct scmi_optee_channel *channel; | |
414 | uint32_t channel_id; | |
415 | int ret; | |
416 | ||
417 | if (!tx) | |
418 | return -ENODEV; | |
419 | ||
420 | channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL); | |
421 | if (!channel) | |
422 | return -ENOMEM; | |
423 | ||
424 | ret = of_property_read_u32_index(cinfo->dev->of_node, "linaro,optee-channel-id", | |
425 | 0, &channel_id); | |
426 | if (ret) | |
427 | return ret; | |
428 | ||
429 | cinfo->transport_info = channel; | |
430 | channel->cinfo = cinfo; | |
431 | channel->channel_id = channel_id; | |
432 | mutex_init(&channel->mu); | |
433 | ||
434 | ret = setup_shmem(dev, cinfo, channel); | |
435 | if (ret) | |
436 | return ret; | |
437 | ||
438 | ret = open_session(scmi_optee_private, &channel->tee_session); | |
439 | if (ret) | |
440 | goto err_free_shm; | |
441 | ||
442 | ret = get_channel(channel); | |
443 | if (ret) | |
444 | goto err_close_sess; | |
445 | ||
bf322084 CM |
446 | /* Enable polling */ |
447 | cinfo->no_completion_irq = true; | |
448 | ||
5f90f189 EC |
449 | mutex_lock(&scmi_optee_private->mu); |
450 | list_add(&channel->link, &scmi_optee_private->channel_list); | |
451 | mutex_unlock(&scmi_optee_private->mu); | |
452 | ||
453 | return 0; | |
454 | ||
455 | err_close_sess: | |
456 | close_session(scmi_optee_private, channel->tee_session); | |
457 | err_free_shm: | |
458 | if (channel->tee_shm) | |
459 | tee_shm_free(channel->tee_shm); | |
460 | ||
461 | return ret; | |
462 | } | |
463 | ||
464 | static int scmi_optee_chan_free(int id, void *p, void *data) | |
465 | { | |
466 | struct scmi_chan_info *cinfo = p; | |
467 | struct scmi_optee_channel *channel = cinfo->transport_info; | |
468 | ||
469 | mutex_lock(&scmi_optee_private->mu); | |
470 | list_del(&channel->link); | |
471 | mutex_unlock(&scmi_optee_private->mu); | |
472 | ||
473 | close_session(scmi_optee_private, channel->tee_session); | |
474 | ||
475 | if (channel->tee_shm) { | |
476 | tee_shm_free(channel->tee_shm); | |
477 | channel->tee_shm = NULL; | |
478 | } | |
479 | ||
480 | cinfo->transport_info = NULL; | |
481 | channel->cinfo = NULL; | |
482 | ||
483 | scmi_free_channel(cinfo, data, id); | |
484 | ||
485 | return 0; | |
486 | } | |
487 | ||
5f90f189 EC |
488 | static int scmi_optee_send_message(struct scmi_chan_info *cinfo, |
489 | struct scmi_xfer *xfer) | |
490 | { | |
491 | struct scmi_optee_channel *channel = cinfo->transport_info; | |
5f90f189 EC |
492 | int ret; |
493 | ||
494 | mutex_lock(&channel->mu); | |
5f90f189 | 495 | |
5e114ad9 EC |
496 | if (channel->tee_shm) { |
497 | msg_tx_prepare(channel->req.msg, xfer); | |
498 | ret = invoke_process_msg_channel(channel, msg_command_size(xfer)); | |
499 | } else { | |
500 | shmem_tx_prepare(channel->req.shmem, xfer); | |
501 | ret = invoke_process_smt_channel(channel); | |
502 | } | |
503 | ||
bf322084 CM |
504 | if (ret) |
505 | mutex_unlock(&channel->mu); | |
5f90f189 EC |
506 | |
507 | return ret; | |
508 | } | |
509 | ||
510 | static void scmi_optee_fetch_response(struct scmi_chan_info *cinfo, | |
511 | struct scmi_xfer *xfer) | |
512 | { | |
513 | struct scmi_optee_channel *channel = cinfo->transport_info; | |
5f90f189 | 514 | |
5e114ad9 | 515 | if (channel->tee_shm) |
75c8f430 | 516 | msg_fetch_response(channel->req.msg, channel->rx_len, xfer); |
5e114ad9 EC |
517 | else |
518 | shmem_fetch_response(channel->req.shmem, xfer); | |
5f90f189 EC |
519 | } |
520 | ||
94d0cd1d CM |
521 | static void scmi_optee_mark_txdone(struct scmi_chan_info *cinfo, int ret, |
522 | struct scmi_xfer *__unused) | |
5f90f189 EC |
523 | { |
524 | struct scmi_optee_channel *channel = cinfo->transport_info; | |
5f90f189 | 525 | |
bf322084 | 526 | mutex_unlock(&channel->mu); |
5f90f189 EC |
527 | } |
528 | ||
529 | static struct scmi_transport_ops scmi_optee_ops = { | |
530 | .link_supplier = scmi_optee_link_supplier, | |
531 | .chan_available = scmi_optee_chan_available, | |
532 | .chan_setup = scmi_optee_chan_setup, | |
533 | .chan_free = scmi_optee_chan_free, | |
534 | .send_message = scmi_optee_send_message, | |
bf322084 | 535 | .mark_txdone = scmi_optee_mark_txdone, |
5f90f189 EC |
536 | .fetch_response = scmi_optee_fetch_response, |
537 | .clear_channel = scmi_optee_clear_channel, | |
5f90f189 EC |
538 | }; |
539 | ||
540 | static int scmi_optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) | |
541 | { | |
542 | return ver->impl_id == TEE_IMPL_ID_OPTEE; | |
543 | } | |
544 | ||
545 | static int scmi_optee_service_probe(struct device *dev) | |
546 | { | |
547 | struct scmi_optee_agent *agent; | |
548 | struct tee_context *tee_ctx; | |
549 | int ret; | |
550 | ||
551 | /* Only one SCMI OP-TEE device allowed */ | |
552 | if (scmi_optee_private) { | |
553 | dev_err(dev, "An SCMI OP-TEE device was already initialized: only one allowed\n"); | |
554 | return -EBUSY; | |
555 | } | |
556 | ||
557 | tee_ctx = tee_client_open_context(NULL, scmi_optee_ctx_match, NULL, NULL); | |
558 | if (IS_ERR(tee_ctx)) | |
559 | return -ENODEV; | |
560 | ||
561 | agent = devm_kzalloc(dev, sizeof(*agent), GFP_KERNEL); | |
562 | if (!agent) { | |
563 | ret = -ENOMEM; | |
564 | goto err; | |
565 | } | |
566 | ||
567 | agent->dev = dev; | |
568 | agent->tee_ctx = tee_ctx; | |
569 | INIT_LIST_HEAD(&agent->channel_list); | |
61bc76be | 570 | mutex_init(&agent->mu); |
5f90f189 EC |
571 | |
572 | ret = get_capabilities(agent); | |
573 | if (ret) | |
574 | goto err; | |
575 | ||
576 | /* Ensure agent resources are all visible before scmi_optee_private is */ | |
577 | smp_mb(); | |
578 | scmi_optee_private = agent; | |
579 | ||
580 | return 0; | |
581 | ||
582 | err: | |
583 | tee_client_close_context(tee_ctx); | |
584 | ||
585 | return ret; | |
586 | } | |
587 | ||
588 | static int scmi_optee_service_remove(struct device *dev) | |
589 | { | |
590 | struct scmi_optee_agent *agent = scmi_optee_private; | |
591 | ||
592 | if (!scmi_optee_private) | |
593 | return -EINVAL; | |
594 | ||
595 | if (!list_empty(&scmi_optee_private->channel_list)) | |
596 | return -EBUSY; | |
597 | ||
598 | /* Ensure cleared reference is visible before resources are released */ | |
599 | smp_store_mb(scmi_optee_private, NULL); | |
600 | ||
601 | tee_client_close_context(agent->tee_ctx); | |
602 | ||
603 | return 0; | |
604 | } | |
605 | ||
606 | static const struct tee_client_device_id scmi_optee_service_id[] = { | |
607 | { | |
608 | UUID_INIT(0xa8cfe406, 0xd4f5, 0x4a2e, | |
609 | 0x9f, 0x8d, 0xa2, 0x5d, 0xc7, 0x54, 0xc0, 0x99) | |
610 | }, | |
611 | { } | |
612 | }; | |
613 | ||
614 | MODULE_DEVICE_TABLE(tee, scmi_optee_service_id); | |
615 | ||
616 | static struct tee_client_driver scmi_optee_driver = { | |
617 | .id_table = scmi_optee_service_id, | |
618 | .driver = { | |
619 | .name = "scmi-optee", | |
620 | .bus = &tee_bus_type, | |
621 | .probe = scmi_optee_service_probe, | |
622 | .remove = scmi_optee_service_remove, | |
623 | }, | |
624 | }; | |
625 | ||
626 | static int scmi_optee_init(void) | |
627 | { | |
628 | return driver_register(&scmi_optee_driver.driver); | |
629 | } | |
630 | ||
631 | static void scmi_optee_exit(void) | |
632 | { | |
633 | if (scmi_optee_private) | |
634 | driver_unregister(&scmi_optee_driver.driver); | |
635 | } | |
636 | ||
637 | const struct scmi_desc scmi_optee_desc = { | |
638 | .transport_exit = scmi_optee_exit, | |
639 | .ops = &scmi_optee_ops, | |
640 | .max_rx_timeout_ms = 30, | |
641 | .max_msg = 20, | |
642 | .max_msg_size = SCMI_OPTEE_MAX_MSG_SIZE, | |
bf322084 | 643 | .sync_cmds_completed_on_ret = true, |
5f90f189 | 644 | }; |