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");