1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2022 Microchip.
6 #include <linux/device.h>
7 #include <linux/kernel.h>
8 #include <linux/module.h>
10 #include <linux/tee_drv.h>
12 #define RTC_INFO_VERSION 0x1
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
20 #define TA_RTC_FEATURE_CORRECTION BIT(0)
22 struct optee_rtc_time {
32 struct optee_rtc_info {
35 struct optee_rtc_time range_min;
36 struct optee_rtc_time range_max;
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
49 struct tee_context *ctx;
55 static int optee_rtc_readtime(struct device *dev, struct rtc_time *tm)
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};
63 inv_arg.func = TA_CMD_RTC_GET_TIME;
64 inv_arg.session = priv->session_id;
65 inv_arg.num_params = 4;
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);
72 ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
73 if (ret < 0 || inv_arg.ret != 0)
74 return ret ? ret : -EPROTO;
76 optee_tm = tee_shm_get_va(priv->shm, 0);
78 return PTR_ERR(optee_tm);
80 if (param[0].u.memref.size != sizeof(*optee_tm))
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);
95 static int optee_rtc_settime(struct device *dev, struct rtc_time *tm)
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;
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;
112 inv_arg.func = TA_CMD_RTC_SET_TIME;
113 inv_arg.session = priv->session_id;
114 inv_arg.num_params = 4;
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);
120 rtc_data = tee_shm_get_va(priv->shm, 0);
121 if (IS_ERR(rtc_data))
122 return PTR_ERR(rtc_data);
124 memcpy(rtc_data, &optee_tm, sizeof(struct optee_rtc_time));
126 ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
127 if (ret < 0 || inv_arg.ret != 0)
128 return ret ? ret : -EPROTO;
133 static int optee_rtc_readoffset(struct device *dev, long *offset)
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};
140 if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
143 inv_arg.func = TA_CMD_RTC_GET_OFFSET;
144 inv_arg.session = priv->session_id;
145 inv_arg.num_params = 4;
147 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
149 ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
150 if (ret < 0 || inv_arg.ret != 0)
151 return ret ? ret : -EPROTO;
153 *offset = param[0].u.value.a;
158 static int optee_rtc_setoffset(struct device *dev, long offset)
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};
165 if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
168 inv_arg.func = TA_CMD_RTC_SET_OFFSET;
169 inv_arg.session = priv->session_id;
170 inv_arg.num_params = 4;
172 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
173 param[0].u.value.a = offset;
175 ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
176 if (ret < 0 || inv_arg.ret != 0)
177 return ret ? ret : -EPROTO;
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,
189 static int optee_rtc_read_info(struct device *dev, struct rtc_device *rtc,
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;
199 inv_arg.func = TA_CMD_RTC_GET_INFO;
200 inv_arg.session = priv->session_id;
201 inv_arg.num_params = 4;
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);
207 ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
208 if (ret < 0 || inv_arg.ret != 0)
209 return ret ? ret : -EPROTO;
211 info = tee_shm_get_va(priv->shm, 0);
213 return PTR_ERR(info);
215 if (param[0].u.memref.size != sizeof(*info))
218 if (info->version != RTC_INFO_VERSION)
221 *features = info->features;
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,
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,
233 static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
235 if (ver->impl_id == TEE_IMPL_ID_OPTEE)
241 static int optee_rtc_probe(struct device *dev)
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;
250 memset(&sess_arg, 0, sizeof(sess_arg));
252 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
256 rtc = devm_rtc_allocate_device(dev);
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))
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;
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);
275 priv->session_id = sess_arg.session;
277 shm = tee_shm_alloc_kernel_buf(priv->ctx, sizeof(struct optee_rtc_info));
279 dev_err(priv->dev, "tee_shm_alloc_kernel_buf failed\n");
286 dev_set_drvdata(dev, priv);
288 rtc->ops = &optee_rtc_ops;
290 err = optee_rtc_read_info(dev, rtc, &priv->features);
292 dev_err(dev, "Failed to get RTC features from OP-TEE\n");
296 err = devm_rtc_register_device(rtc);
301 * We must clear this bit after registering because rtc_register_device
302 * will set it if it sees that .set_offset is provided.
304 if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
305 clear_bit(RTC_FEATURE_CORRECTION, rtc->features);
310 tee_shm_free(priv->shm);
312 tee_client_close_session(priv->ctx, priv->session_id);
314 tee_client_close_context(priv->ctx);
319 static int optee_rtc_remove(struct device *dev)
321 struct optee_rtc *priv = dev_get_drvdata(dev);
323 tee_client_close_session(priv->ctx, priv->session_id);
324 tee_client_close_context(priv->ctx);
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)},
335 MODULE_DEVICE_TABLE(tee, optee_rtc_id_table);
337 static struct tee_client_driver optee_rtc_driver = {
338 .id_table = optee_rtc_id_table,
341 .bus = &tee_bus_type,
342 .probe = optee_rtc_probe,
343 .remove = optee_rtc_remove,
347 static int __init optee_rtc_mod_init(void)
349 return driver_register(&optee_rtc_driver.driver);
352 static void __exit optee_rtc_mod_exit(void)
354 driver_unregister(&optee_rtc_driver.driver);
357 module_init(optee_rtc_mod_init);
358 module_exit(optee_rtc_mod_exit);
360 MODULE_LICENSE("GPL v2");
361 MODULE_AUTHOR("Clément Léger <clement.leger@bootlin.com>");
362 MODULE_DESCRIPTION("OP-TEE based RTC driver");