Merge tag 'amlogic-drivers-for-v5.20' of git://git.kernel.org/pub/scm/linux/kernel...
[platform/kernel/linux-rpi.git] / drivers / rtc / rtc-optee.c
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
22 struct 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
32 struct 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  */
47 struct 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
55 static 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(priv->ctx, &inv_arg, param);
73         if (ret < 0 || inv_arg.ret != 0)
74                 return ret ? ret : -EPROTO;
75
76         optee_tm = tee_shm_get_va(priv->shm, 0);
77         if (IS_ERR(optee_tm))
78                 return PTR_ERR(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(tm->tm_mday, tm->tm_mon, tm->tm_year);
91
92         return 0;
93 }
94
95 static 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(priv->shm, 0);
121         if (IS_ERR(rtc_data))
122                 return PTR_ERR(rtc_data);
123
124         memcpy(rtc_data, &optee_tm, sizeof(struct optee_rtc_time));
125
126         ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
127         if (ret < 0 || inv_arg.ret != 0)
128                 return ret ? ret : -EPROTO;
129
130         return 0;
131 }
132
133 static 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(priv->ctx, &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
158 static 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(priv->ctx, &inv_arg, param);
176         if (ret < 0 || inv_arg.ret != 0)
177                 return ret ? ret : -EPROTO;
178
179         return 0;
180 }
181
182 static 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
189 static 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(priv->ctx, &inv_arg, param);
208         if (ret < 0 || inv_arg.ret != 0)
209                 return ret ? ret : -EPROTO;
210
211         info = tee_shm_get_va(priv->shm, 0);
212         if (IS_ERR(info))
213                 return PTR_ERR(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(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,
225                                   tm->tm_sec);
226         tm = &info->range_max;
227         rtc->range_max = mktime64(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,
228                                   tm->tm_sec);
229
230         return 0;
231 }
232
233 static 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
241 static 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, sizeof(*priv), GFP_KERNEL);
253         if (!priv)
254                 return -ENOMEM;
255
256         rtc = devm_rtc_allocate_device(dev);
257         if (IS_ERR(rtc))
258                 return PTR_ERR(rtc);
259
260         /* Open context with TEE driver */
261         priv->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);
262         if (IS_ERR(priv->ctx))
263                 return -ENODEV;
264
265         /* Open session with rtc Trusted App */
266         export_uuid(sess_arg.uuid, &rtc_device->id.uuid);
267         sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
268
269         ret = tee_client_open_session(priv->ctx, &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(priv->ctx, sizeof(struct optee_rtc_info));
278         if (IS_ERR(shm)) {
279                 dev_err(priv->dev, "tee_shm_alloc_kernel_buf failed\n");
280                 err = PTR_ERR(shm);
281                 goto out_sess;
282         }
283
284         priv->shm = shm;
285         priv->dev = dev;
286         dev_set_drvdata(dev, priv);
287
288         rtc->ops = &optee_rtc_ops;
289
290         err = optee_rtc_read_info(dev, rtc, &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, rtc->features);
306
307         return 0;
308
309 out_shm:
310         tee_shm_free(priv->shm);
311 out_sess:
312         tee_client_close_session(priv->ctx, priv->session_id);
313 out_ctx:
314         tee_client_close_context(priv->ctx);
315
316         return err;
317 }
318
319 static int optee_rtc_remove(struct device *dev)
320 {
321         struct optee_rtc *priv = dev_get_drvdata(dev);
322
323         tee_client_close_session(priv->ctx, priv->session_id);
324         tee_client_close_context(priv->ctx);
325
326         return 0;
327 }
328
329 static 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
335 MODULE_DEVICE_TABLE(tee, optee_rtc_id_table);
336
337 static 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
347 static int __init optee_rtc_mod_init(void)
348 {
349         return driver_register(&optee_rtc_driver.driver);
350 }
351
352 static void __exit optee_rtc_mod_exit(void)
353 {
354         driver_unregister(&optee_rtc_driver.driver);
355 }
356
357 module_init(optee_rtc_mod_init);
358 module_exit(optee_rtc_mod_exit);
359
360 MODULE_LICENSE("GPL v2");
361 MODULE_AUTHOR("Clément Léger <clement.leger@bootlin.com>");
362 MODULE_DESCRIPTION("OP-TEE based RTC driver");