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