735 lines · 19.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2022 Microchip. */ #include <linux/device.h>#include <linux/kernel.h>#include <linux/kthread.h>#include <linux/module.h>#include <linux/rtc.h>#include <linux/tee_drv.h> #define RTC_INFO_VERSION			0x1 #define TA_RTC_FEATURE_CORRECTION		BIT(0)#define TA_RTC_FEATURE_ALARM			BIT(1)#define TA_RTC_FEATURE_WAKEUP_ALARM		BIT(2) enum rtc_optee_pta_cmd {	/*  PTA_CMD_RTC_GET_INFO - Get RTC information	 *	 * [out]        memref[0]  RTC buffer memory reference containing a struct pta_rtc_info	 */	PTA_CMD_RTC_GET_INF = 0x0, 	/*	 * PTA_CMD_RTC_GET_TIME - Get time from RTC	 *	 * [out]    memref[0]  RTC buffer memory reference containing a struct pta_rtc_time	 */	PTA_CMD_RTC_GET_TIME = 0x1, 	/*	 * PTA_CMD_RTC_SET_TIME - Set time from RTC	 *	 * [in]     memref[0]  RTC buffer memory reference containing a struct pta_rtc_time to be	 *                     used as RTC time	 */	PTA_CMD_RTC_SET_TIME = 0x2, 	/*	 * PTA_CMD_RTC_GET_OFFSET - Get RTC offset	 *	 * [out]    value[0].a  RTC offset (signed 32bit value)	 */	PTA_CMD_RTC_GET_OFFSET = 0x3, 	/*	 * PTA_CMD_RTC_SET_OFFSET - Set RTC offset	 *	 * [in]     value[0].a  RTC offset to be set (signed 32bit value)	 */	PTA_CMD_RTC_SET_OFFSET = 0x4, 	/*	 * PTA_CMD_RTC_READ_ALARM - Read RTC alarm	 *	 * [out]     memref[0]  RTC buffer memory reference containing a struct pta_rtc_alarm	 */	PTA_CMD_RTC_READ_ALARM = 0x5, 	/*	 * PTA_CMD_RTC_SET_ALARM - Set RTC alarm	 *	 * [in]     memref[0]  RTC buffer memory reference containing a struct pta_rtc_alarm to be	 *                     used as RTC alarm	 */	PTA_CMD_RTC_SET_ALARM = 0x6, 	/*	 * PTA_CMD_RTC_ENABLE_ALARM - Enable Alarm	 *	 * [in]     value[0].a  RTC IRQ flag (uint32_t), 0 to disable the alarm, 1 to enable	 */	PTA_CMD_RTC_ENABLE_ALARM = 0x7, 	/*	 * PTA_CMD_RTC_WAIT_ALARM - Get alarm event	 *	 * [out]     value[0].a  RTC wait alarm return status (uint32_t):	 *                       - 0: No alarm event	 *                       - 1: Alarm event occurred	 *                       - 2: Alarm event canceled	 */	PTA_CMD_RTC_WAIT_ALARM = 0x8, 	/*	 * PTA_CMD_RTC_CANCEL_WAIT - Cancel wait for alarm event	 */	PTA_CMD_RTC_CANCEL_WAIT = 0x9, 	/*	 * PTA_CMD_RTC_SET_WAKE_ALARM_STATUS - Set RTC wake alarm status flag	 *	 * [in]     value[0].a RTC IRQ wake alarm flag (uint32_t), 0 to disable the wake up	 *                     capability, 1 to enable.	 */	PTA_CMD_RTC_SET_WAKE_ALARM_STATUS = 0xA,}; enum rtc_wait_alarm_status {	WAIT_ALARM_RESET = 0x0,	WAIT_ALARM_ALARM_OCCURRED = 0x1,	WAIT_ALARM_CANCELED = 0x2,}; struct optee_rtc_time {	u32 tm_sec;	u32 tm_min;	u32 tm_hour;	u32 tm_mday;	u32 tm_mon;	u32 tm_year;	u32 tm_wday;}; struct optee_rtc_alarm {	u8 enabled;	u8 pending;	struct optee_rtc_time time;}; struct optee_rtc_info {	u64 version;	u64 features;	struct optee_rtc_time range_min;	struct optee_rtc_time range_max;}; /** * struct optee_rtc - OP-TEE RTC private data * @dev:		OP-TEE based RTC device. * @ctx:		OP-TEE context handler. * @session_id:		RTC TA session identifier. * @session2_id:	RTC wait alarm session identifier. * @shm:		Memory pool shared with RTC device. * @features:		Bitfield of RTC features * @alarm_task:		RTC wait alamr task. * @rtc:		RTC device. */struct optee_rtc {	struct device *dev;	struct tee_context *ctx;	u32 session_id;	u32 session2_id;	struct tee_shm *shm;	u64 features;	struct task_struct *alarm_task;	struct rtc_device *rtc;}; static int optee_rtc_readtime(struct device *dev, struct rtc_time *tm){	struct optee_rtc *priv = dev_get_drvdata(dev);	struct tee_ioctl_invoke_arg inv_arg = {0};	struct optee_rtc_time *optee_tm;	struct tee_param param[4] = {0};	int ret; 	inv_arg.func = PTA_CMD_RTC_GET_TIME;	inv_arg.session = priv->session_id;	inv_arg.num_params = 4; 	/* Fill invoke cmd params */	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;	param[0].u.memref.shm = priv->shm;	param[0].u.memref.size = sizeof(struct optee_rtc_time); 	ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);	if (ret < 0 || inv_arg.ret != 0)		return ret ? ret : -EPROTO; 	optee_tm = tee_shm_get_va(priv->shm, 0);	if (IS_ERR(optee_tm))		return PTR_ERR(optee_tm); 	if (param[0].u.memref.size != sizeof(*optee_tm))		return -EPROTO; 	tm->tm_sec = optee_tm->tm_sec;	tm->tm_min = optee_tm->tm_min;	tm->tm_hour = optee_tm->tm_hour;	tm->tm_mday = optee_tm->tm_mday;	tm->tm_mon = optee_tm->tm_mon;	tm->tm_year = optee_tm->tm_year - 1900;	tm->tm_wday = optee_tm->tm_wday;	tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); 	return 0;} static int optee_rtc_settime(struct device *dev, struct rtc_time *tm){	struct optee_rtc *priv = dev_get_drvdata(dev);	struct tee_ioctl_invoke_arg inv_arg = {0};	struct tee_param param[4] = {0};	struct optee_rtc_time *optee_tm;	int ret; 	inv_arg.func = PTA_CMD_RTC_SET_TIME;	inv_arg.session = priv->session_id;	inv_arg.num_params = 4; 	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;	param[0].u.memref.shm = priv->shm;	param[0].u.memref.size = sizeof(struct optee_rtc_time); 	optee_tm = tee_shm_get_va(priv->shm, 0);	if (IS_ERR(optee_tm))		return PTR_ERR(optee_tm); 	optee_tm->tm_min = tm->tm_min;	optee_tm->tm_sec = tm->tm_sec;	optee_tm->tm_hour = tm->tm_hour;	optee_tm->tm_mday = tm->tm_mday;	optee_tm->tm_mon = tm->tm_mon;	optee_tm->tm_year = tm->tm_year + 1900;	optee_tm->tm_wday = tm->tm_wday; 	ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);	if (ret < 0 || inv_arg.ret != 0)		return ret ? ret : -EPROTO; 	return 0;} static int optee_rtc_readoffset(struct device *dev, long *offset){	struct optee_rtc *priv = dev_get_drvdata(dev);	struct tee_ioctl_invoke_arg inv_arg = {0};	struct tee_param param[4] = {0};	int ret; 	if (!(priv->features & TA_RTC_FEATURE_CORRECTION))		return -EOPNOTSUPP; 	inv_arg.func = PTA_CMD_RTC_GET_OFFSET;	inv_arg.session = priv->session_id;	inv_arg.num_params = 4; 	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; 	ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);	if (ret < 0 || inv_arg.ret != 0)		return ret ? ret : -EPROTO; 	*offset = param[0].u.value.a; 	return 0;} static int optee_rtc_setoffset(struct device *dev, long offset){	struct optee_rtc *priv = dev_get_drvdata(dev);	struct tee_ioctl_invoke_arg inv_arg = {0};	struct tee_param param[4] = {0};	int ret; 	if (!(priv->features & TA_RTC_FEATURE_CORRECTION))		return -EOPNOTSUPP; 	inv_arg.func = PTA_CMD_RTC_SET_OFFSET;	inv_arg.session = priv->session_id;	inv_arg.num_params = 4; 	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;	param[0].u.value.a = offset; 	ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);	if (ret < 0 || inv_arg.ret != 0)		return ret ? ret : -EPROTO; 	return 0;} static int optee_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm){	struct optee_rtc *priv = dev_get_drvdata(dev);	struct tee_ioctl_invoke_arg inv_arg = {0};	struct optee_rtc_alarm *optee_alarm;	struct tee_param param[1] = {0};	int ret; 	if (!(priv->features & TA_RTC_FEATURE_ALARM))		return -EOPNOTSUPP; 	inv_arg.func = PTA_CMD_RTC_READ_ALARM;	inv_arg.session = priv->session_id;	inv_arg.num_params = 1; 	/* Fill invoke cmd params */	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;	param[0].u.memref.shm = priv->shm;	param[0].u.memref.size = sizeof(struct optee_rtc_alarm); 	ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);	if (ret < 0 || inv_arg.ret != 0)		return ret ? ret : -EPROTO; 	optee_alarm = tee_shm_get_va(priv->shm, 0);	if (IS_ERR(optee_alarm))		return PTR_ERR(optee_alarm); 	if (param[0].u.memref.size != sizeof(*optee_alarm))		return -EPROTO; 	alarm->enabled = optee_alarm->enabled;	alarm->pending = optee_alarm->pending;	alarm->time.tm_sec = optee_alarm->time.tm_sec;	alarm->time.tm_min = optee_alarm->time.tm_min;	alarm->time.tm_hour = optee_alarm->time.tm_hour;	alarm->time.tm_mday = optee_alarm->time.tm_mday;	alarm->time.tm_mon = optee_alarm->time.tm_mon;	alarm->time.tm_year = optee_alarm->time.tm_year - 1900;	alarm->time.tm_wday = optee_alarm->time.tm_wday;	alarm->time.tm_yday = rtc_year_days(alarm->time.tm_mday,					    alarm->time.tm_mon,					    alarm->time.tm_year); 	return 0;} static int optee_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm){	struct optee_rtc *priv = dev_get_drvdata(dev);	struct tee_ioctl_invoke_arg inv_arg = {0};	struct optee_rtc_alarm *optee_alarm;	struct tee_param param[1] = {0};	int ret; 	if (!(priv->features & TA_RTC_FEATURE_ALARM))		return -EOPNOTSUPP; 	inv_arg.func = PTA_CMD_RTC_SET_ALARM;	inv_arg.session = priv->session_id;	inv_arg.num_params = 1; 	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;	param[0].u.memref.shm = priv->shm;	param[0].u.memref.size = sizeof(struct optee_rtc_alarm); 	optee_alarm = tee_shm_get_va(priv->shm, 0);	if (IS_ERR(optee_alarm))		return PTR_ERR(optee_alarm); 	optee_alarm->enabled = alarm->enabled;	optee_alarm->pending = alarm->pending;	optee_alarm->time.tm_sec = alarm->time.tm_sec;	optee_alarm->time.tm_min = alarm->time.tm_min;	optee_alarm->time.tm_hour = alarm->time.tm_hour;	optee_alarm->time.tm_mday = alarm->time.tm_mday;	optee_alarm->time.tm_mon = alarm->time.tm_mon;	optee_alarm->time.tm_year = alarm->time.tm_year + 1900;	optee_alarm->time.tm_wday = alarm->time.tm_wday; 	ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);	if (ret < 0 || inv_arg.ret != 0)		return ret ? ret : -EPROTO; 	return 0;} static int optee_rtc_enable_alarm(struct device *dev, unsigned int enabled){	struct optee_rtc *priv = dev_get_drvdata(dev);	struct tee_ioctl_invoke_arg inv_arg = {0};	struct tee_param param[1] = {0};	int ret; 	if (!(priv->features & TA_RTC_FEATURE_ALARM))		return -EOPNOTSUPP; 	inv_arg.func = PTA_CMD_RTC_ENABLE_ALARM;	inv_arg.session = priv->session_id;	inv_arg.num_params = 1; 	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;	param[0].u.value.a = (bool)enabled; 	ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);	if (ret < 0 || inv_arg.ret != 0)		return ret ? ret : -EPROTO; 	return 0;} static const struct rtc_class_ops optee_rtc_ops = {	.read_time		= optee_rtc_readtime,	.set_time		= optee_rtc_settime,	.set_offset		= optee_rtc_setoffset,	.read_offset		= optee_rtc_readoffset,	.read_alarm		= optee_rtc_read_alarm,	.set_alarm		= optee_rtc_set_alarm,	.alarm_irq_enable	= optee_rtc_enable_alarm,}; static int optee_rtc_wait_alarm(struct device *dev, int *return_status){	struct optee_rtc *priv = dev_get_drvdata(dev);	struct tee_ioctl_invoke_arg inv_arg = {0};	struct tee_param param[1] = {0};	int ret; 	if (!(priv->features & TA_RTC_FEATURE_ALARM))		return -EOPNOTSUPP; 	inv_arg.func = PTA_CMD_RTC_WAIT_ALARM;	inv_arg.session = priv->session2_id;	inv_arg.num_params = 1; 	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; 	ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);	if (ret < 0 || inv_arg.ret != 0)		return ret ? ret : -EPROTO; 	*return_status = param[0].u.value.a; 	return 0;} static int optee_rtc_cancel_wait_alarm(struct device *dev){	struct optee_rtc *priv = dev_get_drvdata(dev);	struct tee_ioctl_invoke_arg inv_arg = {0};	struct tee_param param[1] = {0};	int ret; 	if (!(priv->features & TA_RTC_FEATURE_ALARM))		return -EOPNOTSUPP; 	inv_arg.func = PTA_CMD_RTC_CANCEL_WAIT;	inv_arg.session = priv->session_id;	inv_arg.num_params = 0; 	ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);	if (ret < 0 || inv_arg.ret != 0)		return ret ? ret : -EPROTO; 	return 0;} static int optee_rtc_set_alarm_wake_status(struct device *dev, bool status){	struct optee_rtc *priv = dev_get_drvdata(dev);	struct tee_ioctl_invoke_arg inv_arg = {0};	struct tee_param param[1] = {0};	int ret; 	if (!(priv->features & TA_RTC_FEATURE_ALARM))		return -EOPNOTSUPP; 	inv_arg.func = PTA_CMD_RTC_SET_WAKE_ALARM_STATUS;	inv_arg.session = priv->session_id;	inv_arg.num_params = 1; 	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;	param[0].u.value.a = status; 	ret = tee_client_invoke_func(priv->ctx, &inv_arg, param); 	if (ret < 0 || inv_arg.ret != 0)		return ret ? ret : -EPROTO; 	return 0;} static int optee_rtc_handle_alarm_event(void *data){	struct optee_rtc *priv = (struct optee_rtc *)data;	int wait_alarm_return_status = 0;	int ret; 	while (!kthread_should_stop()) {		ret = optee_rtc_wait_alarm(priv->dev, &wait_alarm_return_status);		if (ret) {			dev_err(priv->dev, "Failed to wait for alarm: %d\n", ret);			return ret;		}		switch (wait_alarm_return_status) {		case WAIT_ALARM_ALARM_OCCURRED:			dev_dbg(priv->dev, "Alarm occurred\n");			rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF);			break;		case WAIT_ALARM_CANCELED:			dev_dbg(priv->dev, "Alarm canceled\n");			break;		default:			dev_warn(priv->dev, "Unknown return status: %d\n",				 wait_alarm_return_status);			break;		}	} 	return 0;} static int optee_rtc_read_info(struct device *dev, struct rtc_device *rtc,			       u64 *features){

Lines 1–500 of 735.