diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig
index 61b507c18780..3a995d7f0d74 100644
--- a/drivers/tee/Kconfig
+++ b/drivers/tee/Kconfig
@@ -16,5 +16,6 @@ if TEE
 source "drivers/tee/optee/Kconfig"
 source "drivers/tee/amdtee/Kconfig"
 source "drivers/tee/tstee/Kconfig"
+source "drivers/tee/qcomtee/Kconfig"
 
 endif
diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile
index 5488cba30bd2..74e987f8f7ea 100644
--- a/drivers/tee/Makefile
+++ b/drivers/tee/Makefile
@@ -6,3 +6,4 @@ tee-objs += tee_shm_pool.o
 obj-$(CONFIG_OPTEE) += optee/
 obj-$(CONFIG_AMDTEE) += amdtee/
 obj-$(CONFIG_ARM_TSTEE) += tstee/
+obj-$(CONFIG_QCOMTEE) += qcomtee/
diff --git a/drivers/tee/qcomtee/Kconfig b/drivers/tee/qcomtee/Kconfig
new file mode 100644
index 000000000000..d180a6d07d33
--- /dev/null
+++ b/drivers/tee/qcomtee/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Qualcomm Trusted Execution Environment Configuration
+config QCOMTEE
+	tristate "Qualcomm TEE Support"
+	select QCOM_SCM
+	help
+	  This option enables the Qualcomm Trusted Execution Environment (QTEE)
+	  driver. It provides an API to access services offered by QTEE and any
+	  loaded Trusted Applications (TAs), as well as exporting kernel
+	  services to QTEE.
diff --git a/drivers/tee/qcomtee/Makefile b/drivers/tee/qcomtee/Makefile
new file mode 100644
index 000000000000..7dc5e6373042
--- /dev/null
+++ b/drivers/tee/qcomtee/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_QCOMTEE) += qcomtee.o
+qcomtee-objs += async.o
+qcomtee-objs += core.o
+qcomtee-objs += qcom_scm.o
+qcomtee-objs += release.o
diff --git a/drivers/tee/qcomtee/async.c b/drivers/tee/qcomtee/async.c
new file mode 100644
index 000000000000..218ec0209722
--- /dev/null
+++ b/drivers/tee/qcomtee/async.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include "qcomtee_private.h"
+#include "qcomtee_msg.h"
+
+#define QCOM_TEE_ASYNC_VERSION_1_0 0x00010000U	/* Major: 0x0001, Minor: 0x0000. */
+#define QCOM_TEE_ASYNC_VERSION_1_1 0x00010001U	/* Major: 0x0001, Minor: 0x0001. */
+#define QCOM_TEE_ASYNC_VERSION_1_2 0x00010002U	/* Major: 0x0001, Minor: 0x0002. */
+#define QCOM_TEE_ASYNC_VERSION QCOM_TEE_ASYNC_VERSION_1_2 /* Current Version. */
+
+#define QCOM_TEE_ASYNC_VERSION_MAJOR(n) upper_16_bits(n)
+#define QCOM_TEE_ASYNC_VERSION_MINOR(n) lower_16_bits(n)
+
+/**
+ * struct qcom_tee_async_msg_hdr - Asynchronous message header format.
+ * @version: current async protocol version of remote endpoint
+ * @op: async operation
+ *
+ * @version specifies the endpoints (QTEE or driver) supported async protocol, e.g.
+ * if QTEE set @version to %QCOM_TEE_ASYNC_VERSION_1_1, QTEE handles operations
+ * supported in %QCOM_TEE_ASYNC_VERSION_1_1 or %QCOM_TEE_ASYNC_VERSION_1_0.
+ * @op determins the message format.
+ */
+struct qcom_tee_async_msg_hdr {
+	u32 version;
+	u32 op;
+};
+
+/**
+ * struct qcom_tee_async_release_msg - Release asynchronous message.
+ * @hdr: message header as &struct qcom_tee_async_msg_hdr
+ * @counts: number of objects in @object_ids
+ * @object_ids: array of object ids should be released
+ *
+ * Available in Major = 0x0001, Minor >= 0x0000.
+ */
+struct qcom_tee_async_release_msg {
+	struct qcom_tee_async_msg_hdr hdr;
+	u32 counts;
+	u32 object_ids[] __counted_by(counts);
+};
+
+/**
+ * qcom_tee_get_async_buffer() - Get start of the asynchronous message in outbound buffer.
+ * @oic: context used for current invocation
+ * @async_buffer: return buffer to extract from or fill in async messages
+ *
+ * If @oic is used for direct object invocation, whole outbound buffer is available for
+ * async message. If @oic is used for callback request, the tail of outbound buffer (after
+ * the callback request message) is available for async message.
+ */
+static void qcom_tee_get_async_buffer(struct qcom_tee_object_invoke_ctx *oic,
+				      struct qcom_tee_buffer *async_buffer)
+{
+	struct qcom_tee_msg_callback *msg;
+	unsigned int offset;
+	int i;
+
+	if (!(oic->flags & QCOM_TEE_OIC_FLAG_BUSY)) {
+		/* The outbound buffer is empty. Using the whole buffer. */
+		offset = 0;
+	} else {
+		msg = (struct qcom_tee_msg_callback *)oic->out_msg.addr;
+
+		/* Start offset in a message for buffer arguments. */
+		offset = qcom_tee_msg_buffer_args(struct qcom_tee_msg_callback,
+						  qcom_tee_msg_args(msg));
+
+		/* Add size of IB arguments. */
+		qcom_tee_msg_for_each_input_buffer(i, msg)
+			offset += qcom_tee_msg_offset_align(msg->args[i].b.size);
+
+		/* Add size of OB arguments. */
+		qcom_tee_msg_for_each_output_buffer(i, msg)
+			offset += qcom_tee_msg_offset_align(msg->args[i].b.size);
+	}
+
+	async_buffer->addr = oic->out_msg.addr + offset;
+	async_buffer->size = oic->out_msg.size - offset;
+}
+
+/**
+ * qcom_tee_async_release_handler() - Process QTEE async requests for releasing objects.
+ * @oic: context used for current invocation
+ * @msg: async message for object release
+ * @size: size of the async buffer available
+ *
+ * Return: Size of outbound buffer used when processing @msg.
+ */
+static size_t qcom_tee_async_release_handler(struct qcom_tee_object_invoke_ctx *oic,
+					     struct qcom_tee_async_msg_hdr *async_msg, size_t size)
+{
+	struct qcom_tee_async_release_msg *msg = (struct qcom_tee_async_release_msg *)async_msg;
+	struct qcom_tee_object *object;
+	int i;
+
+	for (i = 0; i < msg->counts; i++) {
+		object = qcom_tee_idx_erase(msg->object_ids[i]);
+		qcom_tee_object_put(object);
+	}
+
+	return struct_size_t(struct qcom_tee_async_release_msg, object_ids, i);
+}
+
+/**
+ * qcom_tee_fetch_async_reqs() - Fetch and process asynchronous messages.
+ * @oic: context used for current invocation
+ *
+ * It looks for handler to process the requested operations in the async message.
+ * Currently, only support async release requests.
+ */
+void qcom_tee_fetch_async_reqs(struct qcom_tee_object_invoke_ctx *oic)
+{
+	struct qcom_tee_async_msg_hdr *async_msg;
+	struct qcom_tee_buffer async_buffer;
+	size_t consumed, used = 0;
+
+	qcom_tee_get_async_buffer(oic, &async_buffer);
+
+	while (async_buffer.size - used > sizeof(struct qcom_tee_async_msg_hdr)) {
+		async_msg = (struct qcom_tee_async_msg_hdr *)(async_buffer.addr + used);
+
+		if (QCOM_TEE_ASYNC_VERSION_MAJOR(async_msg->version) !=
+		    QCOM_TEE_ASYNC_VERSION_MAJOR(QCOM_TEE_ASYNC_VERSION))
+			goto out;
+
+		switch (async_msg->op) {
+		case QCOM_TEE_MSG_OBJECT_OP_RELEASE:
+			consumed = qcom_tee_async_release_handler(oic, async_msg,
+								  async_buffer.size - used);
+			break;
+		default:
+			/* Unsupported operations. */
+			goto out;
+		}
+
+		/* Supported operation but unable to parse the message. */
+		if (!consumed)
+			goto out;
+
+		used += qcom_tee_msg_offset_align(consumed);
+	}
+
+ out:
+	/* Reset the async messages buffer so async requests do not loopback to QTEE. */
+	memzero_explicit(async_buffer.addr, async_buffer.size);
+}
diff --git a/drivers/tee/qcomtee/core.c b/drivers/tee/qcomtee/core.c
new file mode 100644
index 000000000000..a949ef4cceee
--- /dev/null
+++ b/drivers/tee/qcomtee/core.c
@@ -0,0 +1,928 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/firmware/qcom/qcom_tzmem.h>
+#include <linux/init.h>
+#include <linux/kobject.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/xarray.h>
+
+#include "qcomtee_msg.h"
+#include "qcomtee_private.h"
+
+/* Static instance of object represents QTEE root object. */
+struct qcom_tee_object qcom_tee_object_root = {
+	.name = "root",
+	.object_type = QCOM_TEE_OBJECT_TYPE_ROOT,
+	.info.qtee_id = QCOM_TEE_MSG_OBJECT_ROOT,
+};
+EXPORT_SYMBOL_GPL(qcom_tee_object_root);
+
+/* Next argument of type @type after index @i. */
+int qcom_tee_next_arg_type(struct qcom_tee_arg *u, int i, enum qcom_tee_arg_type type)
+{
+	while (u[i].type != QCOM_TEE_ARG_TYPE_INV && u[i].type != type)
+		i++;
+	return i;
+}
+
+/* QTEE expects IDs with QCOM_TEE_MSG_OBJECT_NS_BIT set for object of
+ * QCOM_TEE_OBJECT_TYPE_CB_OBJECT type.
+ */
+#define QCOM_TEE_OBJECT_ID_START	(QCOM_TEE_MSG_OBJECT_NS_BIT + 1)
+#define QCOM_TEE_OBJECT_ID_END		(UINT_MAX)
+
+#define QCOM_TEE_OBJECT_SET(p, type, ...) __QCOM_TEE_OBJECT_SET(p, type, ##__VA_ARGS__, 0UL)
+#define __QCOM_TEE_OBJECT_SET(p, type, optr, ...) do { \
+		(p)->object_type = (type); \
+		(p)->info.qtee_id = (unsigned long)(optr); \
+	} while (0)
+
+static struct qcom_tee_object *qcom_tee_object_alloc(void)
+{
+	struct qcom_tee_object *object;
+
+	object = kzalloc(sizeof(*object), GFP_KERNEL);
+	if (object) {
+		QCOM_TEE_OBJECT_SET(object, QCOM_TEE_OBJECT_TYPE_NULL);
+		kref_init(&object->refcount);
+	}
+
+	return object;
+}
+
+void qcom_tee_object_free(struct qcom_tee_object *object)
+{
+	kfree(object->name);
+	kfree(object);
+}
+
+static void qcom_tee_object_release(struct kref *refcount)
+{
+	struct qcom_tee_object *object;
+	struct module *owner;
+	const char *name;
+
+	object = container_of(refcount, struct qcom_tee_object, refcount);
+
+	synchronize_rcu();
+
+	switch (typeof_qcom_tee_object(object)) {
+	case QCOM_TEE_OBJECT_TYPE_TEE:
+		qcom_tee_release_tee_object(object);
+
+		break;
+	case QCOM_TEE_OBJECT_TYPE_CB_OBJECT:
+		/* Copy, as after release we should not access object. */
+		name = object->name;
+		owner = object->owner;
+
+		if (object->ops->release)
+			object->ops->release(object);
+
+		module_put(owner);
+		kfree_const(name);
+
+		break;
+	case QCOM_TEE_OBJECT_TYPE_ROOT:
+	case QCOM_TEE_OBJECT_TYPE_NULL:
+	default:
+		break;
+	}
+}
+
+/**
+ * qcom_tee_object_get() - Increase object's refcount.
+ * @object: object to increase the refcount
+ */
+int qcom_tee_object_get(struct qcom_tee_object *object)
+{
+	if (object != NULL_QCOM_TEE_OBJECT &&
+	    object != ROOT_QCOM_TEE_OBJECT)
+		return kref_get_unless_zero(&object->refcount);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_tee_object_get);
+
+/**
+ * qcom_tee_object_put() - Decrease object's refcount
+ * @object: object to decrease the refcount
+ */
+void qcom_tee_object_put(struct qcom_tee_object *object)
+{
+	if (object != NULL_QCOM_TEE_OBJECT &&
+	    object != ROOT_QCOM_TEE_OBJECT)
+		kref_put(&object->refcount, qcom_tee_object_release);
+}
+EXPORT_SYMBOL_GPL(qcom_tee_object_put);
+
+/* ''Local Object Table''. */
+/* Object from kernel that are exported to QTEE are assigned an id and stored in
+ * xa_qcom_local_objects (kernel object table). QTEE uses this id to reference the
+ * object using qcom_tee_local_object_get.
+ */
+static DEFINE_XARRAY_ALLOC(xa_qcom_local_objects);
+
+static int qcom_tee_idx_alloc(u32 *idx, struct qcom_tee_object *object)
+{
+	static u32 xa_last_id = QCOM_TEE_OBJECT_ID_START;
+
+	/* Every id allocated here, has QCOM_TEE_MSG_OBJECT_NS_BIT set. */
+	return xa_alloc_cyclic(&xa_qcom_local_objects, idx, object,
+		XA_LIMIT(QCOM_TEE_OBJECT_ID_START, QCOM_TEE_OBJECT_ID_END),
+			&xa_last_id, GFP_KERNEL);
+}
+
+struct qcom_tee_object *qcom_tee_idx_erase(u32 idx)
+{
+	if (idx < QCOM_TEE_OBJECT_ID_START || idx > QCOM_TEE_OBJECT_ID_END)
+		return NULL_QCOM_TEE_OBJECT;
+
+	return xa_erase(&xa_qcom_local_objects, idx);
+}
+
+/**
+ * qcom_tee_object_id_get() - Get an id for an object to sent to QTEE.
+ * @object: object to get its id.
+ * @object_id: object id.
+ *
+ * For object hosted in REE, they are added to object table, and the idx in the
+ * object table is used as id. For object hosted in QTEE, use the QTEE id stored in
+ * @object. This is called on a path to QTEE to construct a message, see
+ * qcom_tee_prepare_msg() and qcom_tee_update_msg().
+ *
+ * Return: On success return 0 or <0 on failure.
+ */
+static int qcom_tee_object_id_get(struct qcom_tee_object *object, unsigned int *object_id)
+{
+	u32 idx;
+
+	switch (typeof_qcom_tee_object(object)) {
+	case QCOM_TEE_OBJECT_TYPE_CB_OBJECT:
+		if (qcom_tee_idx_alloc(&idx, object) < 0)
+			return -ENOSPC;
+
+		*object_id = idx;
+
+		break;
+	case QCOM_TEE_OBJECT_TYPE_ROOT:
+	case QCOM_TEE_OBJECT_TYPE_TEE:
+		*object_id = object->info.qtee_id;
+
+		break;
+	case QCOM_TEE_OBJECT_TYPE_NULL:
+		*object_id = QCOM_TEE_MSG_OBJECT_NULL;
+
+		break;
+	}
+
+	return 0;
+}
+
+/* Release object id assigned in qcom_tee_object_id_get. */
+static void qcom_tee_object_id_put(unsigned int object_id)
+{
+	qcom_tee_idx_erase(object_id);
+}
+
+/**
+ * qcom_tee_local_object_get() - Get an object in REE referenced by the id.
+ * @object_id: object id.
+ *
+ * It is called on behalf of QTEE to obtain instance of object for an id. It is
+ * called on a path from QTEE to construct an argument of &struct qcom_tee_arg,
+ * see qcom_tee_update_args() and qcom_tee_prepare_args().
+ *
+ * It increases the object's refcount on success.
+ *
+ * Return: On error returns %NULL_QCOM_TEE_OBJECT. On success, the object.
+ */
+static struct qcom_tee_object *qcom_tee_local_object_get(unsigned int object_id)
+{
+	struct qcom_tee_object *object;
+
+	/* We trust QTEE does not mess the refcounts.
+	 * It does not issue RELEASE request and qcom_tee_object_get(), simultaneously.
+	 */
+
+	object = xa_load(&xa_qcom_local_objects, object_id);
+
+	qcom_tee_object_get(object);
+
+	return object;
+}
+
+/**
+ * __qcom_tee_object_user_init() - Initialize an object for user.
+ * @object: object to initialize.
+ * @ot: type of object as &enum qcom_tee_object_type.
+ * @ops: instance of callbacks.
+ * @fmt: name assigned to the object.
+ *
+ * Return: On success return 0 or <0 on failure.
+ */
+int __qcom_tee_object_user_init(struct qcom_tee_object *object, enum qcom_tee_object_type ot,
+				struct qcom_tee_object_operations *ops, struct module *owner,
+				const char *fmt, ...)
+{
+	va_list ap;
+	int ret;
+
+	kref_init(&object->refcount);
+	QCOM_TEE_OBJECT_SET(object, QCOM_TEE_OBJECT_TYPE_NULL);
+
+	va_start(ap, fmt);
+	switch (ot) {
+	case QCOM_TEE_OBJECT_TYPE_NULL:
+		ret = 0;
+
+		break;
+	case QCOM_TEE_OBJECT_TYPE_CB_OBJECT:
+		object->ops = ops;
+		if (!object->ops->dispatch)
+			return -EINVAL;
+
+		object->owner = owner;
+		if (!try_module_get(object->owner))
+			return -EINVAL;
+
+		/* If failed, "no-name"; it is not really a reason to fail here. */
+		object->name = kvasprintf_const(GFP_KERNEL, fmt, ap);
+		QCOM_TEE_OBJECT_SET(object, QCOM_TEE_OBJECT_TYPE_CB_OBJECT);
+
+		ret = 0;
+		break;
+	case QCOM_TEE_OBJECT_TYPE_ROOT:
+	case QCOM_TEE_OBJECT_TYPE_TEE:
+	default:
+		ret = -EINVAL;
+	}
+	va_end(ap);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(__qcom_tee_object_user_init);
+
+/**
+ * qcom_tee_object_type() - Returns type of object represented by an object id.
+ * @object_id: object id for the object.
+ *
+ * This is similar to typeof_qcom_tee_object() but instead of receiving object
+ * as argument it receives object id. It is used internally on return path
+ * from QTEE.
+ *
+ * Return: Returns type of object referenced by @object_id.
+ */
+static enum qcom_tee_object_type qcom_tee_object_type(unsigned int object_id)
+{
+	if (object_id == QCOM_TEE_MSG_OBJECT_NULL)
+		return QCOM_TEE_OBJECT_TYPE_NULL;
+
+	if (object_id & QCOM_TEE_MSG_OBJECT_NS_BIT)
+		return QCOM_TEE_OBJECT_TYPE_CB_OBJECT;
+
+	return QCOM_TEE_OBJECT_TYPE_TEE;
+}
+
+/**
+ * qcom_tee_object_init() - Initialize an object for QTEE.
+ * @object: return object
+ * @object_id: object id received form QTEE
+ *
+ * Return: On success return 0 or <0 on failure.
+ */
+static int qcom_tee_object_init(struct qcom_tee_object **object, unsigned int object_id)
+{
+	struct qcom_tee_object *qto;
+	int ret = 0;
+
+	switch (qcom_tee_object_type(object_id)) {
+	case QCOM_TEE_OBJECT_TYPE_NULL:
+		*object = NULL_QCOM_TEE_OBJECT;
+
+		break;
+	case QCOM_TEE_OBJECT_TYPE_CB_OBJECT:
+		qto = qcom_tee_local_object_get(object_id);
+		if (qto != NULL_QCOM_TEE_OBJECT)
+			*object = qto;
+		else
+			ret = -EINVAL;
+
+		break;
+	case QCOM_TEE_OBJECT_TYPE_TEE:
+		qto = qcom_tee_object_alloc();
+		if (qto) {
+			/* If failed, "no-name"; it is not really a reason to fail here. */
+			qto->name = kasprintf(GFP_KERNEL, "qcom_tee-%u", object_id);
+			QCOM_TEE_OBJECT_SET(qto, QCOM_TEE_OBJECT_TYPE_TEE, object_id);
+
+			*object = qto;
+		} else {
+			ret = -ENOMEM;
+		}
+
+		break;
+	default:
+
+		break;
+	}
+
+	if (ret)
+		*object = NULL_QCOM_TEE_OBJECT;
+
+	return ret;
+}
+
+/* Marshaling API. */
+/* qcom_tee_prepare_msg  - Prepares inbound buffer for sending to QTEE
+ * qcom_tee_update_args  - Parses QTEE response in inbound buffer
+ * qcom_tee_prepare_args - Parses QTEE request from outbound buffer
+ * qcom_tee_update_msg   - Updates outbound buffer with response for QTEE request
+ */
+
+static int qcom_tee_prepare_msg(struct qcom_tee_object_invoke_ctx *oic,
+				struct qcom_tee_object *object, u32 op,	struct qcom_tee_arg *u)
+{
+	struct qcom_tee_msg_object_invoke *msg;
+	unsigned int object_id;
+	int ib, ob, io, oo;
+	size_t off;
+	int i;
+
+	/* Use input message buffer in 'oic'. */
+	msg = (struct qcom_tee_msg_object_invoke *)oic->in_msg.addr;
+
+	/* Start offset in a message for buffer arguments. */
+	off = qcom_tee_msg_buffer_args(struct qcom_tee_msg_object_invoke, qcom_tee_args_len(u));
+
+	/* Get id of object being invoked. */
+	if (qcom_tee_object_id_get(object, &object_id))
+		return -ENOSPC;
+
+	ib = 0;
+	qcom_tee_arg_for_each_input_buffer(i, u) {
+		void *ptr;
+
+		/* qcom_tee_msg_buffers_alloc() already checked overflow in message! */
+		msg->args[ib].b.offset = off;
+		msg->args[ib].b.size = u[i].b.size;
+
+		ptr = qcom_tee_msg_offset_to_ptr(msg, off);
+		if (!(u[i].flags & QCOM_TEE_ARG_FLAGS_UADDR))
+			memcpy(ptr, u[i].b.addr, u[i].b.size);
+		else if (copy_from_user(ptr, u[i].b.uaddr, u[i].b.size))
+			return -EINVAL;
+
+		off += qcom_tee_msg_offset_align(u[i].b.size);
+		ib++;
+	}
+
+	ob = ib;
+	qcom_tee_arg_for_each_output_buffer(i, u) {
+		/* qcom_tee_msg_buffers_alloc() already checked overflow in message! */
+		msg->args[ob].b.offset = off;
+		msg->args[ob].b.size = u[i].b.size;
+
+		off += qcom_tee_msg_offset_align(u[i].b.size);
+		ob++;
+	}
+
+	io = ob;
+	qcom_tee_arg_for_each_input_object(i, u) {
+		if (qcom_tee_object_id_get(u[i].o, &msg->args[io].o)) {
+			/* Unable to qcom_tee_object_id_get; put whatever we got. */
+			qcom_tee_object_id_put(object_id);
+			for (--io; io >= ob; io--)
+				qcom_tee_object_id_put(msg->args[io].o);
+
+			return -ENOSPC;
+		}
+
+		io++;
+	}
+
+	oo = io;
+	qcom_tee_arg_for_each_output_object(i, u)
+		oo++;
+
+	/* Set object, operation, and argument counts. */
+	qcom_tee_msg_init(msg, object_id, op, ib, ob, io, oo);
+
+	return 0;
+}
+
+static int qcom_tee_update_args(struct qcom_tee_arg *u, struct qcom_tee_object_invoke_ctx *oic)
+{
+	struct qcom_tee_msg_object_invoke *msg;
+	int ib, ob, io, oo;
+	int i, ret = 0;
+
+	/* Use input message buffer in 'oic'. */
+	msg = (struct qcom_tee_msg_object_invoke *)oic->in_msg.addr;
+
+	ib = 0;
+	qcom_tee_arg_for_each_input_buffer(i, u)
+		ib++;
+
+	ob = ib;
+	qcom_tee_arg_for_each_output_buffer(i, u) {
+		void *ptr = qcom_tee_msg_offset_to_ptr(msg, msg->args[ob].b.offset);
+
+		if (!(u[i].flags & QCOM_TEE_ARG_FLAGS_UADDR)) {
+			memcpy(u[i].b.addr, ptr, msg->args[ob].b.size);
+		} else if (copy_to_user(u[i].b.uaddr, ptr, msg->args[ob].b.size)) {
+			/* On ERROR, continue to process arguments to get to output object. */
+			ret = -EINVAL;
+		}
+
+		u[i].b.size = msg->args[ob].b.size;
+		ob++;
+	}
+
+	io = ob;
+	qcom_tee_arg_for_each_input_object(i, u)
+		io++;
+
+	oo = io;
+	qcom_tee_arg_for_each_output_object(i, u) {
+		int err;
+
+		/* On ERROR, continue to process arguments so that we can issue the RELEASE. */
+		err = qcom_tee_object_init(&u[i].o, msg->args[oo].o);
+		if (err)
+			ret = err;
+
+		oo++;
+	}
+
+	return ret;
+}
+
+static int qcom_tee_prepare_args(struct qcom_tee_object_invoke_ctx *oic)
+{
+	int i, ret = 0;
+
+	/* Use output message buffer in 'oic'. */
+	struct qcom_tee_msg_callback *msg = (struct qcom_tee_msg_callback *)oic->out_msg.addr;
+
+	qcom_tee_msg_for_each_input_buffer(i, msg) {
+		oic->u[i].b.addr = qcom_tee_msg_offset_to_ptr(msg, msg->args[i].b.offset);
+		oic->u[i].b.size = msg->args[i].b.size;
+		oic->u[i].type = QCOM_TEE_ARG_TYPE_IB;
+	}
+
+	qcom_tee_msg_for_each_output_buffer(i, msg) {
+		oic->u[i].b.addr = qcom_tee_msg_offset_to_ptr(msg, msg->args[i].b.offset);
+		oic->u[i].b.size = msg->args[i].b.size;
+		oic->u[i].type = QCOM_TEE_ARG_TYPE_OB;
+	}
+
+	qcom_tee_msg_for_each_input_object(i, msg) {
+		int err;
+
+		/* On ERROR, continue to process arguments so that we can issue the RELEASE. */
+		err = qcom_tee_object_init(&oic->u[i].o, msg->args[i].o);
+		if (err)
+			ret = err;
+
+		oic->u[i].type = QCOM_TEE_ARG_TYPE_IO;
+	}
+
+	qcom_tee_msg_for_each_output_object(i, msg)
+		oic->u[i].type = QCOM_TEE_ARG_TYPE_OO;
+
+	/* End of Arguments. */
+	oic->u[i].type = QCOM_TEE_ARG_TYPE_INV;
+
+	return ret;
+}
+
+static int qcom_tee_update_msg(struct qcom_tee_object_invoke_ctx *oic)
+{
+	int ib, ob, io, oo;
+	int i;
+
+	/* Use output message buffer in 'oic'. */
+	struct qcom_tee_msg_callback *msg = (struct qcom_tee_msg_callback *)oic->out_msg.addr;
+
+	ib = 0;
+	qcom_tee_arg_for_each_input_buffer(i, oic->u)
+		ib++;
+
+	ob = ib;
+	qcom_tee_arg_for_each_output_buffer(i, oic->u) {
+		/* Only reduce size; never increase it. */
+		if (msg->args[ob].b.size < oic->u[i].b.size)
+			return -EINVAL;
+
+		msg->args[ob].b.size = oic->u[i].b.size;
+		ob++;
+	}
+
+	io = ob;
+	qcom_tee_arg_for_each_input_object(i, oic->u)
+		io++;
+
+	oo = io;
+	qcom_tee_arg_for_each_output_object(i, oic->u) {
+		if (qcom_tee_object_id_get(oic->u[i].o, &msg->args[oo].o)) {
+			/* Unable to qcom_tee_object_id_get; put whatever we got. */
+			for (--oo; oo >= io; --oo)
+				qcom_tee_object_id_put(msg->args[oo].o);
+
+			return -ENOSPC;
+		}
+
+		oo++;
+	}
+
+	return 0;
+}
+
+/**
+ * define MAX_BUFFER_SIZE - Maximum size of inbound and outbound buffers.
+ *
+ * QTEE transport does not impose any restriction on these buffers. However, if size of
+ * buffers are larger then %MAX_BUFFER_SIZE, user should probably use some other
+ * form of shared memory with QTEE.
+ */
+#define MAX_BUFFER_SIZE SZ_8K
+
+/* Pool to allocate inbound and outbound buffers. */
+static struct qcom_tzmem_pool *tzmem_msg_pool;
+
+static int qcom_tee_msg_buffers_alloc(struct qcom_tee_object_invoke_ctx *oic,
+				      struct qcom_tee_arg *u)
+{
+	size_t size;
+	int i;
+
+	/* Start offset in a message for buffer arguments. */
+	size = qcom_tee_msg_buffer_args(struct qcom_tee_msg_object_invoke, qcom_tee_args_len(u));
+	if (size > MAX_BUFFER_SIZE)
+		return -EINVAL;
+
+	/* Add size of IB arguments. */
+	qcom_tee_arg_for_each_input_buffer(i, u) {
+		size = size_add(size, qcom_tee_msg_offset_align(u[i].b.size));
+		if (size > MAX_BUFFER_SIZE)
+			return -EINVAL;
+	}
+
+	/* Add size of OB arguments. */
+	qcom_tee_arg_for_each_output_buffer(i, u) {
+		size = size_add(size, qcom_tee_msg_offset_align(u[i].b.size));
+		if (size > MAX_BUFFER_SIZE)
+			return -EINVAL;
+	}
+
+	/* QTEE requires inbound buffer size to be page aligned. */
+	size = PAGE_ALIGN(size);
+
+	/* Do allocations. */
+	oic->in_msg.size = size;
+	oic->in_msg.addr = qcom_tzmem_alloc(tzmem_msg_pool, size, GFP_KERNEL);
+	if (!oic->in_msg.addr)
+		return -EINVAL;
+
+	oic->out_msg.size = MAX_BUFFER_SIZE;
+	oic->out_msg.addr = qcom_tzmem_alloc(tzmem_msg_pool, MAX_BUFFER_SIZE, GFP_KERNEL);
+	if (!oic->out_msg.addr) {
+		qcom_tzmem_free(oic->in_msg.addr);
+
+		return -EINVAL;
+	}
+
+	oic->in_msg_paddr = qcom_tzmem_to_phys(oic->in_msg.addr);
+	oic->out_msg_paddr = qcom_tzmem_to_phys(oic->out_msg.addr);
+
+	/* QTEE assume unused buffers are zeroed; Do it now! */
+	memzero_explicit(oic->in_msg.addr, oic->in_msg.size);
+	memzero_explicit(oic->out_msg.addr, oic->out_msg.size);
+
+	return 0;
+}
+
+static void qcom_tee_msg_buffers_free(struct qcom_tee_object_invoke_ctx *oic)
+{
+	qcom_tzmem_free(oic->in_msg.addr);
+	qcom_tzmem_free(oic->out_msg.addr);
+}
+
+static int qcom_tee_msg_buffers_init(void)
+{
+	struct qcom_tzmem_pool_config config = {
+		.policy = QCOM_TZMEM_POLICY_ON_DEMAND,
+		/* 4M seems enough, it is used for QTEE meg header and qcom_tee_msg_arg array. */
+		.max_size = SZ_4M
+	};
+
+	tzmem_msg_pool = qcom_tzmem_pool_new(&config);
+	if (IS_ERR(tzmem_msg_pool))
+		return PTR_ERR(tzmem_msg_pool);
+
+	return 0;
+}
+
+static void qcom_tee_msg_buffers_destroy(void)
+{
+	qcom_tzmem_pool_free(tzmem_msg_pool);
+}
+
+/* Invoke a REE object. */
+static void qcom_tee_object_invoke(struct qcom_tee_object_invoke_ctx *oic,
+				   struct qcom_tee_msg_callback *msg)
+{
+	int i, errno;
+	u32 op;
+
+	/* Get object being invoked. */
+	unsigned int object_id = msg->cxt;
+	struct qcom_tee_object *object;
+
+	/* QTEE can not invoke NULL object or objects it hosts. */
+	if (qcom_tee_object_type(object_id) == QCOM_TEE_OBJECT_TYPE_NULL ||
+	    qcom_tee_object_type(object_id) == QCOM_TEE_OBJECT_TYPE_TEE) {
+		errno = -EINVAL;
+		goto out;
+	}
+
+	object = qcom_tee_local_object_get(object_id);
+	if (object == NULL_QCOM_TEE_OBJECT) {
+		errno = -EINVAL;
+		goto out;
+	}
+
+	oic->object = object;
+
+	/* Filter bits used by transport. */
+	op = msg->op & QCOM_TEE_MSG_OBJECT_OP_MASK;
+
+	switch (op) {
+	case QCOM_TEE_MSG_OBJECT_OP_RELEASE:
+		qcom_tee_object_id_put(object_id);
+		qcom_tee_object_put(object);
+		errno = 0;
+
+		break;
+	case QCOM_TEE_MSG_OBJECT_OP_RETAIN:
+		qcom_tee_object_get(object);
+		errno = 0;
+
+		break;
+	default:
+		errno = qcom_tee_prepare_args(oic);
+		if (errno) {
+			/* Unable to parse the message. Release any object arrived as input. */
+			qcom_tee_arg_for_each_input_buffer(i, oic->u)
+				qcom_tee_object_put(oic->u[i].o);
+
+			break;
+		}
+
+		errno = object->ops->dispatch(oic, object, op, oic->u);
+		if (!errno) {
+			/* On SUCCESS, notify object at appropriate time. */
+			oic->flags |= QCOM_TEE_OIC_FLAG_NOTIFY;
+		}
+	}
+
+out:
+
+	oic->errno = errno;
+}
+
+/**
+ * __qcom_tee_object_do_invoke() - Submit an invocation for an object.
+ * @oic: context to use for current invocation.
+ * @object: object being invoked.
+ * @op: requested operation on object.
+ * @u: array of argument for the current invocation.
+ * @result: result returned from QTEE.
+ *
+ * The caller is responsible to keep track of the refcount for each object,
+ * including @object. On return, the caller loses the ownership of all input
+ * object of type %QCOM_TEE_OBJECT_TYPE_CB_OBJECT.
+ *
+ * Return: On success return 0. On error returns -EINVAL and -ENOSPC if unable to initiate
+ * the invocation, -EAGAIN if invocation failed and user may retry the invocation.
+ * Otherwise, -ENODEV on fatal failure.
+ */
+int __qcom_tee_object_do_invoke(struct qcom_tee_object_invoke_ctx *oic,
+				struct qcom_tee_object *object, u32 op,	struct qcom_tee_arg *u,
+				int *result)
+{
+	struct qcom_tee_msg_callback *cb_msg;
+	u64 response_type;
+	int i, ret, errno;
+
+	ret = qcom_tee_msg_buffers_alloc(oic, u);
+	if (ret)
+		return ret;
+
+	ret = qcom_tee_prepare_msg(oic, object, op, u);
+	if (ret)
+		goto out;
+
+	cb_msg = (struct qcom_tee_msg_callback *)oic->out_msg.addr;
+
+	while (1) {
+		if (oic->flags & QCOM_TEE_OIC_FLAG_BUSY) {
+			errno = oic->errno;
+			/* Update output buffer only if result is SUCCESS. */
+			if (!errno)
+				errno = qcom_tee_update_msg(oic);
+
+			qcom_tee_msg_translate_err(cb_msg, errno);
+		}
+
+		/* Invoke remote object. */
+		ret = qcom_tee_object_invoke_ctx_invoke(oic, result, &response_type);
+
+		if (oic->flags & QCOM_TEE_OIC_FLAG_BUSY) {
+			struct qcom_tee_object *qto = oic->object;
+
+			if (qto) {
+				if (oic->flags & QCOM_TEE_OIC_FLAG_NOTIFY) {
+					if (qto->ops->notify)
+						qto->ops->notify(oic, qto, errno || ret);
+				}
+
+				/* Matching get is in qcom_tee_object_invoke. */
+				qcom_tee_object_put(qto);
+			}
+
+			oic->object = NULL_QCOM_TEE_OBJECT;
+			oic->flags &= ~(QCOM_TEE_OIC_FLAG_BUSY | QCOM_TEE_OIC_FLAG_NOTIFY);
+		}
+
+		if (ret) {
+			if (!(oic->flags & QCOM_TEE_OIC_FLAG_SHARED)) {
+				/* Release QCOM_TEE_OBJECT_TYPE_CB_OBJECT input objects. */
+				qcom_tee_arg_for_each_input_object(i, u)
+					if (typeof_qcom_tee_object(u[i].o) ==
+						QCOM_TEE_OBJECT_TYPE_CB_OBJECT)
+						qcom_tee_object_put(u[i].o);
+
+				ret = -EAGAIN;
+			} else {
+				/* On error, there is no clean way to exit. */
+				/* For some reason we can not communicate with QTEE, so we can not
+				 * notify QTEE about the failure and do further cleanup.
+				 */
+				ret = -ENODEV;
+			}
+
+			goto out;
+
+		} else {
+			/* QTEE obtained the ownership of QCOM_TEE_OBJECT_TYPE_CB_OBJECT
+			 * input objects in 'u'. On further failure, QTEE is responsible
+			 * to release them.
+			 */
+			oic->flags |= QCOM_TEE_OIC_FLAG_SHARED;
+		}
+
+		/* Is it a callback request? */
+#define QCOM_TEE_RESULT_INBOUND_REQ_NEEDED 3
+		if (response_type != QCOM_TEE_RESULT_INBOUND_REQ_NEEDED) {
+			if (!*result) {
+				ret = qcom_tee_update_args(u, oic);
+				if (ret) {
+					qcom_tee_arg_for_each_output_object(i, u)
+						qcom_tee_object_put(u[i].o);
+
+					ret = -EAGAIN;
+				}
+			}
+
+			break;
+
+		} else {
+			oic->flags |= QCOM_TEE_OIC_FLAG_BUSY;
+			/* Before dispatching the request, handle any pending async requests. */
+			qcom_tee_fetch_async_reqs(oic);
+			qcom_tee_object_invoke(oic, cb_msg);
+		}
+	}
+
+	qcom_tee_fetch_async_reqs(oic);
+
+out:
+	qcom_tee_msg_buffers_free(oic);
+
+	return ret;
+}
+
+int qcom_tee_object_do_invoke(struct qcom_tee_object_invoke_ctx *oic,
+			      struct qcom_tee_object *object, u32 op, struct qcom_tee_arg *u,
+			      int *result)
+{
+	/* User can not set bits used by transport. */
+	if (op & ~QCOM_TEE_MSG_OBJECT_OP_MASK)
+		return -EINVAL;
+
+	/* User can only invoke QTEE hosted objects. */
+	if (typeof_qcom_tee_object(object) != QCOM_TEE_OBJECT_TYPE_TEE &&
+	    typeof_qcom_tee_object(object) != QCOM_TEE_OBJECT_TYPE_ROOT)
+		return -EINVAL;
+
+	/* User can not issue reserved operations to QTEE. */
+	if (op == QCOM_TEE_MSG_OBJECT_OP_RELEASE || op == QCOM_TEE_MSG_OBJECT_OP_RETAIN)
+		return -EINVAL;
+
+	return  __qcom_tee_object_do_invoke(oic, object, op, u, result);
+}
+EXPORT_SYMBOL_GPL(qcom_tee_object_do_invoke);
+
+/* Dump object table. */
+static ssize_t qcom_tee_object_table_show(struct kobject *kobj,
+					  struct kobj_attribute *attr, char *buf)
+{
+	struct qcom_tee_object *object;
+	unsigned long idx;
+	size_t len = 0;
+
+	xa_for_each_start(&xa_qcom_local_objects, idx, object, QCOM_TEE_OBJECT_ID_START) {
+		len += sysfs_emit_at(buf, len, "%4lx %4d %s\n", idx,
+				     kref_read(&object->refcount),
+				     qcom_tee_object_name(object));
+	}
+
+	return len;
+}
+
+static struct kobj_attribute object_table = __ATTR_RO(qcom_tee_object_table);
+static struct kobj_attribute release = __ATTR_RO(qcom_tee_release_wq);
+static struct attribute *attrs[] = {
+	&object_table.attr,
+	&release.attr,
+	NULL
+};
+
+static struct attribute_group attr_group = {
+	.attrs = attrs,
+};
+
+static struct kobject *qcom_tee_object_invoke_kobj;
+static int __init qcom_tee_object_invoke_init(void)
+{
+	int ret;
+
+	ret = qcom_tee_release_init();
+	if (ret)
+		return ret;
+
+	ret = qcom_tee_msg_buffers_init();
+	if (ret)
+		goto err_release_destroy;
+
+	/* Create '/sys/firmware/qcom_tee'. */
+	qcom_tee_object_invoke_kobj = kobject_create_and_add("qcom_tee", firmware_kobj);
+	if (!qcom_tee_object_invoke_kobj) {
+		ret = -ENOMEM;
+
+		goto err_msg_buffers_destroy;
+	}
+
+	/* Create 'qcom_tee_object_table' and 'qcom_tee_release_wq'. */
+	ret = sysfs_create_group(qcom_tee_object_invoke_kobj, &attr_group);
+	if (ret)
+		goto err_kobject_put;
+
+	return 0;
+
+err_kobject_put:
+	/* Remove '/sys/firmware/qcom_tee'. */
+	kobject_put(qcom_tee_object_invoke_kobj);
+err_msg_buffers_destroy:
+	qcom_tee_msg_buffers_destroy();
+err_release_destroy:
+	qcom_tee_release_destroy();
+
+	return ret;
+}
+module_init(qcom_tee_object_invoke_init);
+
+static void __exit qcom_tee_object_invoke_deinit(void)
+{
+	/* Wait for RELEASE operations for QTEE objects. */
+	qcom_tee_release_destroy();
+	qcom_tee_msg_buffers_destroy();
+	sysfs_remove_group(qcom_tee_object_invoke_kobj, &attr_group);
+	kobject_put(qcom_tee_object_invoke_kobj);
+}
+module_exit(qcom_tee_object_invoke_deinit);
+
+MODULE_AUTHOR("Qualcomm");
+MODULE_DESCRIPTION("QTEE driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tee/qcomtee/qcom_scm.c b/drivers/tee/qcomtee/qcom_scm.c
new file mode 100644
index 000000000000..230faf249095
--- /dev/null
+++ b/drivers/tee/qcomtee/qcom_scm.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/firmware/qcom/qcom_scm.h>
+
+#include "qcomtee_private.h"
+
+int qcom_tee_object_invoke_ctx_invoke(struct qcom_tee_object_invoke_ctx *oic,
+				      int *result, u64 *response_type)
+{
+	int ret;
+	u64 res;
+
+	if (!(oic->flags & QCOM_TEE_OIC_FLAG_BUSY)) {
+		/* Direct QTEE object invocation. */
+		ret = qcom_scm_qtee_invoke_smc(oic->in_msg_paddr,
+					       oic->in_msg.size,
+					       oic->out_msg_paddr,
+					       oic->out_msg.size,
+					       &res, response_type, NULL);
+	} else {
+		/* Submit callback response. */
+		ret = qcom_scm_qtee_callback_response(oic->out_msg_paddr,
+						      oic->out_msg.size,
+						      &res, response_type, NULL);
+	}
+
+	if (ret)
+		pr_err("QTEE returned with %d.\n", ret);
+	else
+		*result = (int)res;
+
+	return ret;
+}
diff --git a/drivers/tee/qcomtee/qcomtee_msg.h b/drivers/tee/qcomtee/qcomtee_msg.h
new file mode 100644
index 000000000000..7c968834ec9d
--- /dev/null
+++ b/drivers/tee/qcomtee/qcomtee_msg.h
@@ -0,0 +1,217 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef QCOMTEE_MSG_H
+#define QCOMTEE_MSG_H
+
+#include <linux/firmware/qcom/qcom_tee.h>
+
+/**
+ * DOC: ''Qualcomm TEE'' (QTEE) Transport Message
+ *
+ * There are two buffers shared with QTEE, inbound and outbound buffers.
+ * The inbound buffer is used for direct object invocation and the outbound buffer is
+ * used to make a request from QTEE to kernel, i.e. callback request.
+ *
+ * The unused tail of the outbound buffer is also used for sending and receiving
+ * asynchronous messages. An asynchronous message is independent from the current
+ * object invocation (i.e. contents of the inbound buffer) or callback request
+ * (i.e. the head of the outbound buffer), see qcom_tee_get_async_buffer(). It is
+ * used by endpoints (QTEE or kernel) as an optimization to reduce number of context
+ * switches between secure and non-secure world.
+ *
+ * For instance, QTEE never sends an explicit callback request to release an object in
+ * kernel. Instead, it sends asynchronous release messages in outbound buffer when QTEE
+ * returns from previous direct object invocation, or append asynchronous release
+ * messages after the current callback request.
+ *
+ * QTEE supports two types of arguments in a message: buffer and object arguments.
+ * Depending on the direction of data flow, they could be input buffer (IO) to QTEE,
+ * output buffer (OB) from QTEE, input object (IO) to QTEE, or output object (OO) from
+ * QTEE. Object arguments hold object ids. Buffer arguments hold (offset, size) pairs
+ * into the inbound or outbound buffers.
+ *
+ * QTEE holds an object table for objects, it hosts and exposes to kernel. An object id
+ * is an index to the object table in QTEE.
+ *
+ * For direct object invocation message format in inbound buffer see
+ * &struct qcom_tee_msg_object_invoke. For callback request message format in outbound
+ * buffer see &struct qcom_tee_msg_callback. For the message format for asynchronous message
+ * in outbound buffer see &struct qcom_tee_async_msg_hdr.
+ */
+
+/**
+ * define QCOM_TEE_MSG_OBJECT_NS_BIT - Non-secure bit
+ *
+ * Object id is a globally unique 32-bit number. Ids referencing objects in kernel should
+ * have %QCOM_TEE_MSG_OBJECT_NS_BIT set.
+ */
+#define QCOM_TEE_MSG_OBJECT_NS_BIT BIT(31)
+
+/* Static object ids recognized by QTEE. */
+#define QCOM_TEE_MSG_OBJECT_NULL (0U)
+#define QCOM_TEE_MSG_OBJECT_ROOT (1U)
+
+/* Definitions from QTEE as part of the transport protocol. */
+
+/* qcom_tee_msg_arg is argument as recognized by QTEE. */
+union qcom_tee_msg_arg {
+	struct {
+		u32 offset;
+		u32 size;
+	} b;
+	u32 o;
+};
+
+/* BI and BO payloads in a QTEE messages should be at 64-bit boundaries. */
+#define qcom_tee_msg_offset_align(o) ALIGN((o), sizeof(u64))
+
+/* Operation for objects is 32-bit. Transport uses upper 16-bits internally. */
+#define QCOM_TEE_MSG_OBJECT_OP_MASK 0x0000FFFFU
+
+/* Reserved Operation IDs sent to QTEE: */
+/* QCOM_TEE_MSG_OBJECT_OP_RELEASE - Reduces the refcount and releases the object.
+ * QCOM_TEE_MSG_OBJECT_OP_RETAIN  - Increases the refcount.
+ *
+ * These operation id are valid for all objects. They are not available outside of this
+ * driver. Developers should use qcom_tee_object_get() and qcom_tee_object_put(), to
+ * achieve the same.
+ */
+
+#define QCOM_TEE_MSG_OBJECT_OP_RELEASE	(QCOM_TEE_MSG_OBJECT_OP_MASK - 0)
+#define QCOM_TEE_MSG_OBJECT_OP_RETAIN	(QCOM_TEE_MSG_OBJECT_OP_MASK - 1)
+
+/**
+ * struct qcom_tee_msg_object_invoke - Direct object invocation message
+ * @ctx: object id hosted in QTEE
+ * @op: operation for the object
+ * @counts: number of different type of arguments in @args
+ * @args: array of arguments
+ *
+ * @counts consists of 4 * 4-bits felids. Bits 0 - 3, is number of input buffers,
+ * bits 4 - 7, is number of output buffers, bits 8 - 11, is number of input objects,
+ * and bits 12 - 15, is number of output objects. Remaining bits should be zero.
+ *
+ * Maximum number of arguments of each type is defined by %QCOM_TEE_ARGS_PER_TYPE.
+ */
+struct qcom_tee_msg_object_invoke {
+	u32 cxt;
+	u32 op;
+	u32 counts;
+	union qcom_tee_msg_arg args[];
+};
+
+/**
+ * struct qcom_tee_msg_callback - Callback request message
+ * @result: result of operation @op on object referenced by @cxt
+ * @cxt: object id hosted in kernel
+ * @op: operation for the object
+ * @counts: number of different type of arguments in @args
+ * @args: array of arguments
+ *
+ * For details of @counts, see &qcom_tee_msg_object_invoke.counts.
+ */
+struct qcom_tee_msg_callback {
+	u32 result;
+	u32 cxt;
+	u32 op;
+	u32 counts;
+	union qcom_tee_msg_arg args[];
+};
+
+/* Offset in the message for the beginning of buffer argument's contents. */
+#define qcom_tee_msg_buffer_args(t, n) \
+	qcom_tee_msg_offset_align(struct_size_t(t, args, n))
+/* Pointer to the beginning of a buffer argument's content at an offset in a message. */
+#define qcom_tee_msg_offset_to_ptr(m, off) ((void *)&((char *)(m))[(off)])
+
+/* Some helpers to manage msg.counts. */
+
+#define QCOM_TEE_MSG_NUM_IB(x) ((x) & 0xfU)
+#define QCOM_TEE_MSG_NUM_OB(x) (((x) >> 4) & 0xfU)
+#define QCOM_TEE_MSG_NUM_IO(x) (((x) >> 8) & 0xfU)
+#define QCOM_TEE_MSG_NUM_OO(x) (((x) >> 12) & 0xfU)
+
+#define QCOM_TEE_MSG_IDX_IB(x) (0U)
+#define QCOM_TEE_MSG_IDX_OB(x) (QCOM_TEE_MSG_IDX_IB(x) + QCOM_TEE_MSG_NUM_IB(x))
+#define QCOM_TEE_MSG_IDX_IO(x) (QCOM_TEE_MSG_IDX_OB(x) + QCOM_TEE_MSG_NUM_OB(x))
+#define QCOM_TEE_MSG_IDX_OO(x) (QCOM_TEE_MSG_IDX_IO(x) + QCOM_TEE_MSG_NUM_IO(x))
+
+#define qcom_tee_msg_for_each(i, c, type)	\
+	for (i = QCOM_TEE_MSG_IDX_##type(c);	\
+	     i < (QCOM_TEE_MSG_IDX_##type(c) + QCOM_TEE_MSG_NUM_##type(c)); \
+	     i++)
+
+#define qcom_tee_msg_for_each_input_buffer(i, m)  qcom_tee_msg_for_each(i, (m)->counts, IB)
+#define qcom_tee_msg_for_each_output_buffer(i, m) qcom_tee_msg_for_each(i, (m)->counts, OB)
+#define qcom_tee_msg_for_each_input_object(i, m)  qcom_tee_msg_for_each(i, (m)->counts, IO)
+#define qcom_tee_msg_for_each_output_object(i, m) qcom_tee_msg_for_each(i, (m)->counts, OO)
+
+/* Sum of arguments in a message. */
+#define qcom_tee_msg_args(m) (QCOM_TEE_MSG_IDX_OO((m)->counts) + QCOM_TEE_MSG_NUM_OO((m)->counts))
+
+static inline void qcom_tee_msg_init(struct qcom_tee_msg_object_invoke *msg, u32 cxt, u32 op,
+				     int in_buffer, int out_buffer, int in_object, int out_object)
+{
+	msg->counts |= (in_buffer & 0xfU);
+	msg->counts |= ((out_buffer - in_buffer) & 0xfU) << 4;
+	msg->counts |= ((in_object - out_buffer) & 0xfU) << 8;
+	msg->counts |= ((out_object - in_object) & 0xfU) << 12;
+	msg->cxt = cxt;
+	msg->op = op;
+}
+
+/* Generic error codes. */
+#define QCOM_TEE_MSG_OK			0	/* non-specific success code. */
+#define QCOM_TEE_MSG_ERROR		1	/* non-specific error. */
+#define QCOM_TEE_MSG_ERROR_INVALID	2	/* unsupported/unrecognized request. */
+#define QCOM_TEE_MSG_ERROR_SIZE_IN	3	/* supplied buffer/string too large. */
+#define QCOM_TEE_MSG_ERROR_SIZE_OUT	4	/* supplied output buffer too small. */
+#define QCOM_TEE_MSG_ERROR_USERBASE	10	/* start of user-defined error range. */
+
+/* Transport layer error codes. */
+#define QCOM_TEE_MSG_ERROR_DEFUNCT	-90	/* object no longer exists. */
+#define QCOM_TEE_MSG_ERROR_ABORT	-91	/* calling thread must exit. */
+#define QCOM_TEE_MSG_ERROR_BADOBJ	-92	/* invalid object context. */
+#define QCOM_TEE_MSG_ERROR_NOSLOTS	-93	/* caller's object table full. */
+#define QCOM_TEE_MSG_ERROR_MAXARGS	-94	/* too many args. */
+#define QCOM_TEE_MSG_ERROR_MAXDATA	-95	/* buffers too large. */
+#define QCOM_TEE_MSG_ERROR_UNAVAIL	-96	/* the request could not be processed. */
+#define QCOM_TEE_MSG_ERROR_KMEM		-97	/* kernel out of memory. */
+#define QCOM_TEE_MSG_ERROR_REMOTE	-98	/* local method sent to remote object. */
+#define QCOM_TEE_MSG_ERROR_BUSY		-99	/* Object is busy. */
+#define QCOM_TEE_MSG_ERROR_TIMEOUT	-103	/* Call Back Object invocation timed out. */
+
+static inline void qcom_tee_msg_translate_err(struct qcom_tee_msg_callback *cb_msg, int err)
+{
+	if (!err) {
+		cb_msg->result = QCOM_TEE_MSG_OK;
+	} else if (err < 0) {
+		/* If err < 0, then it is a transport error. */
+		switch (err) {
+		case -ENOMEM:
+			cb_msg->result = QCOM_TEE_MSG_ERROR_KMEM;
+			break;
+		case -ENODEV:
+			cb_msg->result = QCOM_TEE_MSG_ERROR_DEFUNCT;
+			break;
+		case -ENOSPC:
+		case -EBUSY:
+			cb_msg->result = QCOM_TEE_MSG_ERROR_BUSY;
+			break;
+		case -EBADF:
+		case -EINVAL:
+			cb_msg->result = QCOM_TEE_MSG_ERROR_UNAVAIL;
+			break;
+		default:
+			cb_msg->result =  QCOM_TEE_MSG_ERROR;
+		}
+	} else {
+		/* If err > 0, then it is user defined error, pass it as is. */
+		cb_msg->result = err;
+	}
+}
+
+#endif /* QCOMTEE_MSG_H */
diff --git a/drivers/tee/qcomtee/qcomtee_private.h b/drivers/tee/qcomtee/qcomtee_private.h
new file mode 100644
index 000000000000..e3e4ef51c0b2
--- /dev/null
+++ b/drivers/tee/qcomtee/qcomtee_private.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef QCOM_TEE_PRIVATE_H
+#define QCOM_TEE_PRIVATE_H
+
+#include <linux/firmware/qcom/qcom_tee.h>
+#include <linux/kobject.h>
+#include <linux/tee_core.h>
+
+struct qcom_tee_object *qcom_tee_idx_erase(u32 idx);
+void qcom_tee_object_free(struct qcom_tee_object *object);
+
+/* Process async messages form QTEE. */
+void qcom_tee_fetch_async_reqs(struct qcom_tee_object_invoke_ctx *oic);
+
+int qcom_tee_release_init(void);
+void qcom_tee_release_destroy(void);
+void qcom_tee_release_tee_object(struct qcom_tee_object *object);
+ssize_t qcom_tee_release_wq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
+
+/* SCM call. */
+int qcom_tee_object_invoke_ctx_invoke(struct qcom_tee_object_invoke_ctx *oic,
+				      int *result, u64 *response_type);
+
+/**
+ * __qcom_tee_object_do_invoke() - Submit an invocation for an object.
+ * @oic: context to use for current invocation.
+ * @object: object being invoked.
+ * @op: requested operation on object.
+ * @u: array of argument for the current invocation.
+ * @result: result returned from QTEE.
+ *
+ * Same as qcom_tee_object_do_invoke() without @object and @op is 32-bit,
+ * upper 16-bits are for internal use.
+ *
+ * Return: On success return 0. On error returns -EINVAL and -ENOSPC if unable to initiate
+ * the invocation, -EAGAIN if invocation failed and user can retry the invocation.
+ * Otherwise, -ENODEV on fatal failure.
+ */
+int __qcom_tee_object_do_invoke(struct qcom_tee_object_invoke_ctx *oic,
+				struct qcom_tee_object *object, u32 op,	struct qcom_tee_arg *u,
+				int *result);
+
+#endif /* QCOM_TEE_PRIVATE_H */
diff --git a/drivers/tee/qcomtee/release.c b/drivers/tee/qcomtee/release.c
new file mode 100644
index 000000000000..f2e048418e23
--- /dev/null
+++ b/drivers/tee/qcomtee/release.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "qcomtee_private.h"
+#include "qcomtee_msg.h"
+
+static struct workqueue_struct *qcom_tee_release_wq;
+
+/* Number of all release requests pending for processing. */
+static atomic_t qcom_tee_pending_releases = ATOMIC_INIT(0);
+
+/* qcom_tee_object_do_release makes direct object invocation to release an object. */
+static void qcom_tee_destroy_user_object(struct work_struct *work)
+{
+	static struct qcom_tee_object_invoke_ctx oic;
+	static struct qcom_tee_arg args[1] = { 0 };
+	struct qcom_tee_object *object;
+	int ret, result;
+
+	object = container_of(work, struct qcom_tee_object, work);
+
+	ret = __qcom_tee_object_do_invoke(&oic, object, QCOM_TEE_MSG_OBJECT_OP_RELEASE, args,
+					  &result);
+
+	/* Is it safe to retry the release? */
+	if (ret == -EAGAIN) {
+		queue_work(qcom_tee_release_wq, &object->work);
+	} else {
+		if (ret || result)
+			pr_err("%s: %s release failed, ret = %d (%x).\n",
+			       __func__, qcom_tee_object_name(object), ret, result);
+
+		atomic_dec(&qcom_tee_pending_releases);
+		qcom_tee_object_free(object);
+	}
+}
+
+/* qcom_tee_release_tee_object puts object in release work queue. */
+void qcom_tee_release_tee_object(struct qcom_tee_object *object)
+{
+	INIT_WORK(&object->work, qcom_tee_destroy_user_object);
+	atomic_inc(&qcom_tee_pending_releases);
+	queue_work(qcom_tee_release_wq, &object->work);
+}
+
+ssize_t qcom_tee_release_wq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	return sysfs_emit(buf, "%d\n", atomic_read(&qcom_tee_pending_releases));
+}
+
+int qcom_tee_release_init(void)
+{
+	qcom_tee_release_wq = alloc_ordered_workqueue("qcom_tee_release_wq", 0);
+	if (!qcom_tee_release_wq)
+		return -ENOMEM;
+
+	return 0;
+}
+
+void qcom_tee_release_destroy(void)
+{
+	/* It drains the wq. */
+	destroy_workqueue(qcom_tee_release_wq);
+}
diff --git a/include/linux/firmware/qcom/qcom_tee.h b/include/linux/firmware/qcom/qcom_tee.h
new file mode 100644
index 000000000000..90e5e10a0e62
--- /dev/null
+++ b/include/linux/firmware/qcom/qcom_tee.h
@@ -0,0 +1,284 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __QCOM_TEE_H
+#define __QCOM_TEE_H
+
+#include <linux/kref.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+
+struct qcom_tee_object;
+
+/**
+ * DOC: Overview
+ *
+ * qcom_tee_object provides object ref-counting, id allocation for objects hosted in
+ * REE, and necessary message marshaling for Qualcomm TEE (QTEE).
+ *
+ * To invoke an object in QTEE, user calls qcom_tee_object_do_invoke() while passing
+ * an instance of &struct qcom_tee_object and the requested operation + arguments.
+ *
+ * After the boot, QTEE provides a static object %ROOT_QCOM_TEE_OBJECT (type of
+ * %QCOM_TEE_OBJECT_TYPE_ROOT). The root object is invoked to pass user's credentials and
+ * obtain other instances of &struct qcom_tee_object (type of %QCOM_TEE_OBJECT_TYPE_TEE)
+ * that represents services and TAs in QTEE, see &enum qcom_tee_object_type.
+ *
+ * The object received from QTEE are refcounted. So the owner of these objects can
+ * issue qcom_tee_object_get(), to increase the refcount, and pass objects to other
+ * clients, or issue qcom_tee_object_put() to decrease the refcount, and releasing
+ * the resources in QTEE.
+ *
+ * REE can host services accessible to QTEE. A driver should embed an instance of
+ * &struct qcom_tee_object in the struct it wants to export to QTEE (it is called
+ * callback object). It issues qcom_tee_object_user_init() to set the dispatch()
+ * operation for the callback object and set its type to %QCOM_TEE_OBJECT_TYPE_CB_OBJECT.
+ *
+ * core.c holds an object table for callback objects. An object id is assigned
+ * to each callback object which is an index to the object table. QTEE uses these ids
+ * to reference or invoke callback objects.
+ *
+ * If QTEE invoke a callback object in REE, the dispatch() operation is called in the
+ * context of thread that called qcom_tee_object_do_invoke(), originally.
+ */
+
+/**
+ * enum qcom_tee_object_typ - Object types.
+ * @QCOM_TEE_OBJECT_TYPE_TEE: object hosted on QTEE.
+ * @QCOM_TEE_OBJECT_TYPE_CB_OBJECT: object hosted on REE.
+ * @QCOM_TEE_OBJECT_TYPE_ROOT: 'primordial' object.
+ * @QCOM_TEE_OBJECT_TYPE_NULL: NULL object.
+ *
+ * Primordial object is used for bootstrapping the IPC connection between a REE
+ * and QTEE. It is invoked by REE when it wants to get a 'client env'.
+ */
+enum qcom_tee_object_type {
+	QCOM_TEE_OBJECT_TYPE_TEE,
+	QCOM_TEE_OBJECT_TYPE_CB_OBJECT,
+	QCOM_TEE_OBJECT_TYPE_ROOT,
+	QCOM_TEE_OBJECT_TYPE_NULL,
+};
+
+/**
+ * enum qcom_tee_arg_type - Type of QTEE argument.
+ * @QCOM_TEE_ARG_TYPE_INV: invalid type.
+ * @QCOM_TEE_ARG_TYPE_IB: input buffer (IO).
+ * @QCOM_TEE_ARG_TYPE_OO: output object (OO).
+ * @QCOM_TEE_ARG_TYPE_OB: output buffer (OB).
+ * @QCOM_TEE_ARG_TYPE_IO: input object (IO).
+ *
+ * Use invalid type to specify end of argument array.
+ */
+enum qcom_tee_arg_type {
+	QCOM_TEE_ARG_TYPE_INV = 0,
+	QCOM_TEE_ARG_TYPE_OB,
+	QCOM_TEE_ARG_TYPE_OO,
+	QCOM_TEE_ARG_TYPE_IB,
+	QCOM_TEE_ARG_TYPE_IO,
+	QCOM_TEE_ARG_TYPE_NR,
+};
+
+/**
+ * define QCOM_TEE_ARGS_PER_TYPE - Maximum arguments of specific type.
+ *
+ * QTEE transport protocol limits maximum number of argument of specific type
+ * (i.e. IB, OB, IO, and OO).
+ */
+#define QCOM_TEE_ARGS_PER_TYPE 16
+
+/* Maximum arguments that can fit in a QTEE message, ignoring the type. */
+#define QCOM_TEE_ARGS_MAX (QCOM_TEE_ARGS_PER_TYPE * (QCOM_TEE_ARG_TYPE_NR - 1))
+
+struct qcom_tee_buffer {
+	union {
+		void *addr;
+		void __user *uaddr;
+	};
+	size_t size;
+};
+
+/**
+ * struct qcom_tee_arg - Argument for QTEE object invocation.
+ * @type: type of argument as &enum qcom_tee_arg_type.
+ * @flags: extra flags.
+ * @b: address and size if type of argument is buffer.
+ * @o: object instance if type of argument is object.
+ *
+ * &qcom_tee_arg.flags only accept %QCOM_TEE_ARG_FLAGS_UADDR for now which states
+ * that &qcom_tee_arg.b contains userspace address in uaddr.
+ */
+struct qcom_tee_arg {
+	enum qcom_tee_arg_type type;
+/* 'b.uaddr' holds a __user address. */
+#define QCOM_TEE_ARG_FLAGS_UADDR 1
+	unsigned int flags;
+	union {
+		struct qcom_tee_buffer b;
+		struct qcom_tee_object *o;
+	};
+};
+
+static inline int qcom_tee_args_len(struct qcom_tee_arg *args)
+{
+	int i = 0;
+
+	while (args[i].type != QCOM_TEE_ARG_TYPE_INV)
+		i++;
+	return i;
+}
+
+#define QCOM_TEE_OIC_FLAG_BUSY		BIT(1)	/* Context is busy (callback is in progress). */
+#define QCOM_TEE_OIC_FLAG_NOTIFY	BIT(2)	/* Context needs to notify the current object. */
+#define QCOM_TEE_OIC_FLAG_SHARED	BIT(3)	/* Context has shared state with QTEE. */
+
+struct qcom_tee_object_invoke_ctx {
+	unsigned long flags;
+	int errno;
+
+	/* Current object invoked in this callback context. */
+	struct qcom_tee_object *object;
+
+	/* Arguments passed to dispatch callback (+1 for ending QCOM_TEE_ARG_TYPE_INV). */
+	struct qcom_tee_arg u[QCOM_TEE_ARGS_MAX + 1];
+
+	/* Inbound and Outbound buffers shared with QTEE. */
+	struct qcom_tee_buffer in_msg;		/* Inbound Buffer.  */
+	phys_addr_t in_msg_paddr;		/* Physical address of inbound buffer. */
+	struct qcom_tee_buffer out_msg;		/* Outbound Buffer. */
+	phys_addr_t out_msg_paddr;		/* Physical address of outbound buffer. */
+
+	/* Extra data attached to this context. */
+	void *data;
+};
+
+/**
+ * qcom_tee_object_do_invoke() - Submit an invocation for an object.
+ * @oic: context to use for current invocation.
+ * @object: object being invoked.
+ * @op: requested operation on object.
+ * @u: array of argument for the current invocation.
+ * @result: result returned from QTEE.
+ *
+ * The caller is responsible to keep track of the refcount for each object,
+ * including @object. On return, the caller loses the ownership of all input object of
+ * type %QCOM_TEE_OBJECT_TYPE_CB_OBJECT.
+ *
+ * @object can be of %QCOM_TEE_OBJECT_TYPE_ROOT or %QCOM_TEE_OBJECT_TYPE_TEE types.
+ *
+ * Return: On success return 0. On error returns -EINVAL and -ENOSPC if unable to initiate
+ * the invocation, -EAGAIN if invocation failed and user may retry the invocation.
+ * Otherwise, -ENODEV on fatal failure.
+ */
+int qcom_tee_object_do_invoke(struct qcom_tee_object_invoke_ctx *oic,
+			      struct qcom_tee_object *object, u32 op, struct qcom_tee_arg *u,
+			      int *result);
+
+/**
+ * struct qcom_tee_object_operations - Callback object operations.
+ * @release: release object if QTEE is not using it.
+ * @dispatch: dispatch the operation requested by QTEE.
+ * @notify: report status of any pending response submitted by @dispatch.
+ *
+ * Transport may fail (e.g. object table is full) even after @dispatch successfully submitted
+ * the response. @notify is called to do the necessary cleanup.
+ */
+struct qcom_tee_object_operations {
+	void (*release)(struct qcom_tee_object *object);
+	int  (*dispatch)(struct qcom_tee_object_invoke_ctx *oic,
+			 struct qcom_tee_object *object, u32 op, struct qcom_tee_arg *args);
+	void (*notify)(struct qcom_tee_object_invoke_ctx *oic,
+		       struct qcom_tee_object *object, int err);
+};
+
+/**
+ * struct qcom_tee_object - QTEE or REE object.
+ * @name: object name.
+ * @refcount: reference counter.
+ * @object_type: object type as &enum qcom_tee_object_type.
+ * @info: extra information for object.
+ * @owner: owning module/driver.
+ * @ops: callback operations for object of type %QCOM_TEE_OBJECT_TYPE_CB_OBJECT.
+ * @work: work for async operation on object.
+ *
+ * @work is currently only used for release object of %QCOM_TEE_OBJECT_TYPE_TEE type.
+ */
+struct qcom_tee_object {
+	const char *name;
+	struct kref refcount;
+
+	enum qcom_tee_object_type object_type;
+	union object_info {
+		/* QTEE object id if object_type is %QCOM_TEE_OBJECT_TYPE_TEE. */
+		unsigned long qtee_id;
+	} info;
+
+	struct module *owner;
+	struct qcom_tee_object_operations *ops;
+	struct work_struct work;
+};
+
+/* Static instances of qcom_tee_object objects. */
+#define NULL_QCOM_TEE_OBJECT ((struct qcom_tee_object *)(0))
+extern struct qcom_tee_object qcom_tee_object_root;
+#define ROOT_QCOM_TEE_OBJECT (&qcom_tee_object_root)
+
+static inline enum qcom_tee_object_type typeof_qcom_tee_object(struct qcom_tee_object *object)
+{
+	if (object == NULL_QCOM_TEE_OBJECT)
+		return QCOM_TEE_OBJECT_TYPE_NULL;
+	return object->object_type;
+}
+
+static inline const char *qcom_tee_object_name(struct qcom_tee_object *object)
+{
+	if (object == NULL_QCOM_TEE_OBJECT)
+		return "null";
+
+	if (!object->name)
+		return "no-name";
+	return object->name;
+}
+
+/**
+ * __qcom_tee_object_user_init() - Initialize an object for user.
+ * @object: object to initialize.
+ * @object_type: type of object as &enum qcom_tee_object_type.
+ * @ops: instance of callbacks.
+ * @owner: owning module/driver.
+ * @fmt: name assigned to the object.
+ *
+ * Return: On success return 0 or <0 on failure.
+ */
+int __qcom_tee_object_user_init(struct qcom_tee_object *object, enum qcom_tee_object_type ot,
+				struct qcom_tee_object_operations *ops, struct module *owner,
+				const char *fmt, ...);
+#define qcom_tee_object_user_init(obj, ot, ops, fmt, ...) \
+	__qcom_tee_object_user_init((obj), (ot), (ops), THIS_MODULE, (fmt), __VA_ARGS__)
+
+/* Object release is RCU protected. */
+int qcom_tee_object_get(struct qcom_tee_object *object);
+void qcom_tee_object_put(struct qcom_tee_object *object);
+
+#define qcom_tee_arg_for_each(i, args) \
+	for (i = 0; args[i].type != QCOM_TEE_ARG_TYPE_INV; i++)
+
+/* Next argument of type @type after index @i. */
+int qcom_tee_next_arg_type(struct qcom_tee_arg *u, int i, enum qcom_tee_arg_type type);
+
+/* Iterate over argument of given type. */
+#define qcom_tee_arg_for_each_type(i, args, at)			\
+	for (i = 0, i = qcom_tee_next_arg_type(args, i, at);	\
+		args[i].type != QCOM_TEE_ARG_TYPE_INV;		\
+		i++, i = qcom_tee_next_arg_type(args, i, at))
+
+#define qcom_tee_arg_for_each_input_buffer(i, args)  \
+	qcom_tee_arg_for_each_type(i, args, QCOM_TEE_ARG_TYPE_IB)
+#define qcom_tee_arg_for_each_output_buffer(i, args) \
+	qcom_tee_arg_for_each_type(i, args, QCOM_TEE_ARG_TYPE_OB)
+#define qcom_tee_arg_for_each_input_object(i, args)  \
+	qcom_tee_arg_for_each_type(i, args, QCOM_TEE_ARG_TYPE_IO)
+#define qcom_tee_arg_for_each_output_object(i, args) \
+	qcom_tee_arg_for_each_type(i, args, QCOM_TEE_ARG_TYPE_OO)
+
+#endif /* __QCOM_TEE_H */
