1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2022 Microchip.
4 */
5
6#include <linux/device.h>
7#include <linux/kernel.h>
8#include <linux/module.h>
9#include <linux/rtc.h>
10#include <linux/tee_drv.h>
11
12#define RTC_INFO_VERSION 0x1
13
14#define TA_CMD_RTC_GET_INFO 0x0
15#define TA_CMD_RTC_GET_TIME 0x1
16#define TA_CMD_RTC_SET_TIME 0x2
17#define TA_CMD_RTC_GET_OFFSET 0x3
18#define TA_CMD_RTC_SET_OFFSET 0x4
19
20#define TA_RTC_FEATURE_CORRECTION BIT(0)
21
22struct optee_rtc_time {
23 u32 tm_sec;
24 u32 tm_min;
25 u32 tm_hour;
26 u32 tm_mday;
27 u32 tm_mon;
28 u32 tm_year;
29 u32 tm_wday;
30};
31
32struct optee_rtc_info {
33 u64 version;
34 u64 features;
35 struct optee_rtc_time range_min;
36 struct optee_rtc_time range_max;
37};
38
39/**
40 * struct optee_rtc - OP-TEE RTC private data
41 * @dev: OP-TEE based RTC device.
42 * @ctx: OP-TEE context handler.
43 * @session_id: RTC TA session identifier.
44 * @shm: Memory pool shared with RTC device.
45 * @features: Bitfield of RTC features
46 */
47struct optee_rtc {
48 struct device *dev;
49 struct tee_context *ctx;
50 u32 session_id;
51 struct tee_shm *shm;
52 u64 features;
53};
54
55static int optee_rtc_readtime(struct device *dev, struct rtc_time *tm)
56{
57 struct optee_rtc *priv = dev_get_drvdata(dev);
58 struct tee_ioctl_invoke_arg inv_arg = {0};
59 struct optee_rtc_time *optee_tm;
60 struct tee_param param[4] = {0};
61 int ret;
62
63 inv_arg.func = TA_CMD_RTC_GET_TIME;
64 inv_arg.session = priv->session_id;
65 inv_arg.num_params = 4;
66
67 /* Fill invoke cmd params */
68 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
69 param[0].u.memref.shm = priv->shm;
70 param[0].u.memref.size = sizeof(struct optee_rtc_time);
71
72 ret = tee_client_invoke_func(ctx: priv->ctx, arg: &inv_arg, param);
73 if (ret < 0 || inv_arg.ret != 0)
74 return ret ? ret : -EPROTO;
75
76 optee_tm = tee_shm_get_va(shm: priv->shm, offs: 0);
77 if (IS_ERR(ptr: optee_tm))
78 return PTR_ERR(ptr: optee_tm);
79
80 if (param[0].u.memref.size != sizeof(*optee_tm))
81 return -EPROTO;
82
83 tm->tm_sec = optee_tm->tm_sec;
84 tm->tm_min = optee_tm->tm_min;
85 tm->tm_hour = optee_tm->tm_hour;
86 tm->tm_mday = optee_tm->tm_mday;
87 tm->tm_mon = optee_tm->tm_mon;
88 tm->tm_year = optee_tm->tm_year - 1900;
89 tm->tm_wday = optee_tm->tm_wday;
90 tm->tm_yday = rtc_year_days(day: tm->tm_mday, month: tm->tm_mon, year: tm->tm_year);
91
92 return 0;
93}
94
95static int optee_rtc_settime(struct device *dev, struct rtc_time *tm)
96{
97 struct optee_rtc *priv = dev_get_drvdata(dev);
98 struct tee_ioctl_invoke_arg inv_arg = {0};
99 struct tee_param param[4] = {0};
100 struct optee_rtc_time optee_tm;
101 void *rtc_data;
102 int ret;
103
104 optee_tm.tm_sec = tm->tm_sec;
105 optee_tm.tm_min = tm->tm_min;
106 optee_tm.tm_hour = tm->tm_hour;
107 optee_tm.tm_mday = tm->tm_mday;
108 optee_tm.tm_mon = tm->tm_mon;
109 optee_tm.tm_year = tm->tm_year + 1900;
110 optee_tm.tm_wday = tm->tm_wday;
111
112 inv_arg.func = TA_CMD_RTC_SET_TIME;
113 inv_arg.session = priv->session_id;
114 inv_arg.num_params = 4;
115
116 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
117 param[0].u.memref.shm = priv->shm;
118 param[0].u.memref.size = sizeof(struct optee_rtc_time);
119
120 rtc_data = tee_shm_get_va(shm: priv->shm, offs: 0);
121 if (IS_ERR(ptr: rtc_data))
122 return PTR_ERR(ptr: rtc_data);
123
124 memcpy(rtc_data, &optee_tm, sizeof(struct optee_rtc_time));
125
126 ret = tee_client_invoke_func(ctx: priv->ctx, arg: &inv_arg, param);
127 if (ret < 0 || inv_arg.ret != 0)
128 return ret ? ret : -EPROTO;
129
130 return 0;
131}
132
133static int optee_rtc_readoffset(struct device *dev, long *offset)
134{
135 struct optee_rtc *priv = dev_get_drvdata(dev);
136 struct tee_ioctl_invoke_arg inv_arg = {0};
137 struct tee_param param[4] = {0};
138 int ret;
139
140 if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
141 return -EOPNOTSUPP;
142
143 inv_arg.func = TA_CMD_RTC_GET_OFFSET;
144 inv_arg.session = priv->session_id;
145 inv_arg.num_params = 4;
146
147 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
148
149 ret = tee_client_invoke_func(ctx: priv->ctx, arg: &inv_arg, param);
150 if (ret < 0 || inv_arg.ret != 0)
151 return ret ? ret : -EPROTO;
152
153 *offset = param[0].u.value.a;
154
155 return 0;
156}
157
158static int optee_rtc_setoffset(struct device *dev, long offset)
159{
160 struct optee_rtc *priv = dev_get_drvdata(dev);
161 struct tee_ioctl_invoke_arg inv_arg = {0};
162 struct tee_param param[4] = {0};
163 int ret;
164
165 if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
166 return -EOPNOTSUPP;
167
168 inv_arg.func = TA_CMD_RTC_SET_OFFSET;
169 inv_arg.session = priv->session_id;
170 inv_arg.num_params = 4;
171
172 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
173 param[0].u.value.a = offset;
174
175 ret = tee_client_invoke_func(ctx: priv->ctx, arg: &inv_arg, param);
176 if (ret < 0 || inv_arg.ret != 0)
177 return ret ? ret : -EPROTO;
178
179 return 0;
180}
181
182static const struct rtc_class_ops optee_rtc_ops = {
183 .read_time = optee_rtc_readtime,
184 .set_time = optee_rtc_settime,
185 .set_offset = optee_rtc_setoffset,
186 .read_offset = optee_rtc_readoffset,
187};
188
189static int optee_rtc_read_info(struct device *dev, struct rtc_device *rtc,
190 u64 *features)
191{
192 struct optee_rtc *priv = dev_get_drvdata(dev);
193 struct tee_ioctl_invoke_arg inv_arg = {0};
194 struct tee_param param[4] = {0};
195 struct optee_rtc_info *info;
196 struct optee_rtc_time *tm;
197 int ret;
198
199 inv_arg.func = TA_CMD_RTC_GET_INFO;
200 inv_arg.session = priv->session_id;
201 inv_arg.num_params = 4;
202
203 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
204 param[0].u.memref.shm = priv->shm;
205 param[0].u.memref.size = sizeof(*info);
206
207 ret = tee_client_invoke_func(ctx: priv->ctx, arg: &inv_arg, param);
208 if (ret < 0 || inv_arg.ret != 0)
209 return ret ? ret : -EPROTO;
210
211 info = tee_shm_get_va(shm: priv->shm, offs: 0);
212 if (IS_ERR(ptr: info))
213 return PTR_ERR(ptr: info);
214
215 if (param[0].u.memref.size != sizeof(*info))
216 return -EPROTO;
217
218 if (info->version != RTC_INFO_VERSION)
219 return -EPROTO;
220
221 *features = info->features;
222
223 tm = &info->range_min;
224 rtc->range_min = mktime64(year: tm->tm_year, mon: tm->tm_mon, day: tm->tm_mday, hour: tm->tm_hour, min: tm->tm_min,
225 sec: tm->tm_sec);
226 tm = &info->range_max;
227 rtc->range_max = mktime64(year: tm->tm_year, mon: tm->tm_mon, day: tm->tm_mday, hour: tm->tm_hour, min: tm->tm_min,
228 sec: tm->tm_sec);
229
230 return 0;
231}
232
233static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
234{
235 if (ver->impl_id == TEE_IMPL_ID_OPTEE)
236 return 1;
237 else
238 return 0;
239}
240
241static int optee_rtc_probe(struct device *dev)
242{
243 struct tee_client_device *rtc_device = to_tee_client_device(dev);
244 struct tee_ioctl_open_session_arg sess_arg;
245 struct optee_rtc *priv;
246 struct rtc_device *rtc;
247 struct tee_shm *shm;
248 int ret, err;
249
250 memset(&sess_arg, 0, sizeof(sess_arg));
251
252 priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL);
253 if (!priv)
254 return -ENOMEM;
255
256 rtc = devm_rtc_allocate_device(dev);
257 if (IS_ERR(ptr: rtc))
258 return PTR_ERR(ptr: rtc);
259
260 /* Open context with TEE driver */
261 priv->ctx = tee_client_open_context(NULL, match: optee_ctx_match, NULL, NULL);
262 if (IS_ERR(ptr: priv->ctx))
263 return -ENODEV;
264
265 /* Open session with rtc Trusted App */
266 export_uuid(dst: sess_arg.uuid, src: &rtc_device->id.uuid);
267 sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
268
269 ret = tee_client_open_session(ctx: priv->ctx, arg: &sess_arg, NULL);
270 if (ret < 0 || sess_arg.ret != 0) {
271 dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret);
272 err = -EINVAL;
273 goto out_ctx;
274 }
275 priv->session_id = sess_arg.session;
276
277 shm = tee_shm_alloc_kernel_buf(ctx: priv->ctx, size: sizeof(struct optee_rtc_info));
278 if (IS_ERR(ptr: shm)) {
279 dev_err(priv->dev, "tee_shm_alloc_kernel_buf failed\n");
280 err = PTR_ERR(ptr: shm);
281 goto out_sess;
282 }
283
284 priv->shm = shm;
285 priv->dev = dev;
286 dev_set_drvdata(dev, data: priv);
287
288 rtc->ops = &optee_rtc_ops;
289
290 err = optee_rtc_read_info(dev, rtc, features: &priv->features);
291 if (err) {
292 dev_err(dev, "Failed to get RTC features from OP-TEE\n");
293 goto out_shm;
294 }
295
296 err = devm_rtc_register_device(rtc);
297 if (err)
298 goto out_shm;
299
300 /*
301 * We must clear this bit after registering because rtc_register_device
302 * will set it if it sees that .set_offset is provided.
303 */
304 if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
305 clear_bit(RTC_FEATURE_CORRECTION, addr: rtc->features);
306
307 return 0;
308
309out_shm:
310 tee_shm_free(shm: priv->shm);
311out_sess:
312 tee_client_close_session(ctx: priv->ctx, session: priv->session_id);
313out_ctx:
314 tee_client_close_context(ctx: priv->ctx);
315
316 return err;
317}
318
319static int optee_rtc_remove(struct device *dev)
320{
321 struct optee_rtc *priv = dev_get_drvdata(dev);
322
323 tee_client_close_session(ctx: priv->ctx, session: priv->session_id);
324 tee_client_close_context(ctx: priv->ctx);
325
326 return 0;
327}
328
329static const struct tee_client_device_id optee_rtc_id_table[] = {
330 {UUID_INIT(0xf389f8c8, 0x845f, 0x496c,
331 0x8b, 0xbe, 0xd6, 0x4b, 0xd2, 0x4c, 0x92, 0xfd)},
332 {}
333};
334
335MODULE_DEVICE_TABLE(tee, optee_rtc_id_table);
336
337static struct tee_client_driver optee_rtc_driver = {
338 .id_table = optee_rtc_id_table,
339 .driver = {
340 .name = "optee_rtc",
341 .bus = &tee_bus_type,
342 .probe = optee_rtc_probe,
343 .remove = optee_rtc_remove,
344 },
345};
346
347static int __init optee_rtc_mod_init(void)
348{
349 return driver_register(drv: &optee_rtc_driver.driver);
350}
351
352static void __exit optee_rtc_mod_exit(void)
353{
354 driver_unregister(drv: &optee_rtc_driver.driver);
355}
356
357module_init(optee_rtc_mod_init);
358module_exit(optee_rtc_mod_exit);
359
360MODULE_LICENSE("GPL v2");
361MODULE_AUTHOR("Clément Léger <clement.leger@bootlin.com>");
362MODULE_DESCRIPTION("OP-TEE based RTC driver");
363

source code of linux/drivers/rtc/rtc-optee.c