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