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