obj-$(CONFIG_OPTEE) += optee.o
 optee-objs += core.o
 optee-objs += call.o
+optee-objs += notif.o
 optee-objs += rpc.o
 optee-objs += supp.o
 optee-objs += device.o
 
        /* Unregister OP-TEE specific client devices on TEE bus */
        optee_unregister_devices();
 
+       optee_notif_uninit(optee);
        /*
         * The two devices have to be unregistered before we can free the
         * other resources.
        tee_device_unregister(optee->teedev);
 
        tee_shm_pool_free(optee->pool);
-       optee_wait_queue_exit(&optee->wait_queue);
        optee_supp_uninit(&optee->supp);
        mutex_destroy(&optee->call_queue.mutex);
 }
 
        mutex_init(&optee->ffa.mutex);
        mutex_init(&optee->call_queue.mutex);
        INIT_LIST_HEAD(&optee->call_queue.waiters);
-       optee_wait_queue_init(&optee->wait_queue);
        optee_supp_init(&optee->supp);
        ffa_dev_set_drvdata(ffa_dev, optee);
+       rc = optee_notif_init(optee, OPTEE_DEFAULT_MAX_NOTIF_VALUE);
+       if (rc) {
+               optee_ffa_remove(ffa_dev);
+               return rc;
+       }
 
        rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES);
        if (rc) {
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015-2021, Linaro Limited
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/arm-smccc.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/tee_drv.h>
+#include "optee_private.h"
+
+struct notif_entry {
+       struct list_head link;
+       struct completion c;
+       u_int key;
+};
+
+static bool have_key(struct optee *optee, u_int key)
+{
+       struct notif_entry *entry;
+
+       list_for_each_entry(entry, &optee->notif.db, link)
+               if (entry->key == key)
+                       return true;
+
+       return false;
+}
+
+int optee_notif_wait(struct optee *optee, u_int key)
+{
+       unsigned long flags;
+       struct notif_entry *entry;
+       int rc = 0;
+
+       if (key > optee->notif.max_key)
+               return -EINVAL;
+
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               return -ENOMEM;
+       init_completion(&entry->c);
+       entry->key = key;
+
+       spin_lock_irqsave(&optee->notif.lock, flags);
+
+       /*
+        * If the bit is already set it means that the key has already
+        * been posted and we must not wait.
+        */
+       if (test_bit(key, optee->notif.bitmap)) {
+               clear_bit(key, optee->notif.bitmap);
+               goto out;
+       }
+
+       /*
+        * Check if someone is already waiting for this key. If there is
+        * it's a programming error.
+        */
+       if (have_key(optee, key)) {
+               rc = -EBUSY;
+               goto out;
+       }
+
+       list_add_tail(&entry->link, &optee->notif.db);
+
+       /*
+        * Unlock temporarily and wait for completion.
+        */
+       spin_unlock_irqrestore(&optee->notif.lock, flags);
+       wait_for_completion(&entry->c);
+       spin_lock_irqsave(&optee->notif.lock, flags);
+
+       list_del(&entry->link);
+out:
+       spin_unlock_irqrestore(&optee->notif.lock, flags);
+
+       kfree(entry);
+
+       return rc;
+}
+
+int optee_notif_send(struct optee *optee, u_int key)
+{
+       unsigned long flags;
+       struct notif_entry *entry;
+
+       if (key > optee->notif.max_key)
+               return -EINVAL;
+
+       spin_lock_irqsave(&optee->notif.lock, flags);
+
+       list_for_each_entry(entry, &optee->notif.db, link)
+               if (entry->key == key) {
+                       complete(&entry->c);
+                       goto out;
+               }
+
+       /* Only set the bit in case there where nobody waiting */
+       set_bit(key, optee->notif.bitmap);
+out:
+       spin_unlock_irqrestore(&optee->notif.lock, flags);
+
+       return 0;
+}
+
+int optee_notif_init(struct optee *optee, u_int max_key)
+{
+       spin_lock_init(&optee->notif.lock);
+       INIT_LIST_HEAD(&optee->notif.db);
+       optee->notif.bitmap = bitmap_zalloc(max_key, GFP_KERNEL);
+       if (!optee->notif.bitmap)
+               return -ENOMEM;
+
+       optee->notif.max_key = max_key;
+
+       return 0;
+}
+
+void optee_notif_uninit(struct optee *optee)
+{
+       kfree(optee->notif.bitmap);
+}
 
 
 #define TEEC_ORIGIN_COMMS              0x00000002
 
+/*
+ * This value should be larger than the number threads in secure world to
+ * meet the need from secure world. The number of threads in secure world
+ * are usually not even close to 255 so we should be safe for now.
+ */
+#define OPTEE_DEFAULT_MAX_NOTIF_VALUE  255
+
 typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long,
                                unsigned long, unsigned long, unsigned long,
                                unsigned long, unsigned long,
        struct list_head waiters;
 };
 
-struct optee_wait_queue {
-       /* Serializes access to this struct */
-       struct mutex mu;
+struct optee_notif {
+       u_int max_key;
+       /* Serializes access to the elements below in this struct */
+       spinlock_t lock;
        struct list_head db;
+       u_long *bitmap;
 };
 
 /**
  * @smc:               specific to SMC ABI
  * @ffa:               specific to FF-A ABI
  * @call_queue:                queue of threads waiting to call @invoke_fn
- * @wait_queue:                queue of threads from secure world waiting for a
- *                     secure world sync object
+ * @notif:             notification synchronization struct
  * @supp:              supplicant synchronization struct for RPC to supplicant
  * @pool:              shared memory pool
  * @rpc_arg_count:     If > 0 number of RPC parameters to make room for
                struct optee_ffa ffa;
        };
        struct optee_call_queue call_queue;
-       struct optee_wait_queue wait_queue;
+       struct optee_notif notif;
        struct optee_supp supp;
        struct tee_shm_pool *pool;
        unsigned int rpc_arg_count;
        size_t num_entries;
 };
 
-void optee_wait_queue_init(struct optee_wait_queue *wq);
-void optee_wait_queue_exit(struct optee_wait_queue *wq);
+int optee_notif_init(struct optee *optee, u_int max_key);
+void optee_notif_uninit(struct optee *optee);
+int optee_notif_wait(struct optee *optee, u_int key);
+int optee_notif_send(struct optee *optee, u_int key);
 
 u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params,
                        struct tee_param *param);
 
 #define OPTEE_RPC_CMD_GET_TIME         3
 
 /*
- * Wait queue primitive, helper for secure world to implement a wait queue.
+ * Notification from/to secure world.
  *
- * If secure world needs to wait for a secure world mutex it issues a sleep
- * request instead of spinning in secure world. Conversely is a wakeup
- * request issued when a secure world mutex with a thread waiting thread is
- * unlocked.
+ * If secure world needs to wait for something, for instance a mutex, it
+ * does a notification wait request instead of spinning in secure world.
+ * Conversely can a synchronous notification can be sent when a secure
+ * world mutex with a thread waiting thread is unlocked.
  *
- * Waiting on a key
- * [in]    value[0].a      OPTEE_RPC_WAIT_QUEUE_SLEEP
- * [in]    value[0].b      Wait key
+ * This interface can also be used to wait for a asynchronous notification
+ * which instead is sent via a non-secure interrupt.
  *
- * Waking up a key
- * [in]    value[0].a      OPTEE_RPC_WAIT_QUEUE_WAKEUP
- * [in]    value[0].b      Wakeup key
+ * Waiting on notification
+ * [in]    value[0].a      OPTEE_RPC_NOTIFICATION_WAIT
+ * [in]    value[0].b      notification value
+ *
+ * Sending a synchronous notification
+ * [in]    value[0].a      OPTEE_RPC_NOTIFICATION_SEND
+ * [in]    value[0].b      notification value
  */
-#define OPTEE_RPC_CMD_WAIT_QUEUE       4
-#define OPTEE_RPC_WAIT_QUEUE_SLEEP     0
-#define OPTEE_RPC_WAIT_QUEUE_WAKEUP    1
+#define OPTEE_RPC_CMD_NOTIFICATION     4
+#define OPTEE_RPC_NOTIFICATION_WAIT    0
+#define OPTEE_RPC_NOTIFICATION_SEND    1
 
 /*
  * Suspend execution
 
 #include "optee_private.h"
 #include "optee_rpc_cmd.h"
 
-struct wq_entry {
-       struct list_head link;
-       struct completion c;
-       u32 key;
-};
-
-void optee_wait_queue_init(struct optee_wait_queue *priv)
-{
-       mutex_init(&priv->mu);
-       INIT_LIST_HEAD(&priv->db);
-}
-
-void optee_wait_queue_exit(struct optee_wait_queue *priv)
-{
-       mutex_destroy(&priv->mu);
-}
-
 static void handle_rpc_func_cmd_get_time(struct optee_msg_arg *arg)
 {
        struct timespec64 ts;
 }
 #endif
 
-static struct wq_entry *wq_entry_get(struct optee_wait_queue *wq, u32 key)
-{
-       struct wq_entry *w;
-
-       mutex_lock(&wq->mu);
-
-       list_for_each_entry(w, &wq->db, link)
-               if (w->key == key)
-                       goto out;
-
-       w = kmalloc(sizeof(*w), GFP_KERNEL);
-       if (w) {
-               init_completion(&w->c);
-               w->key = key;
-               list_add_tail(&w->link, &wq->db);
-       }
-out:
-       mutex_unlock(&wq->mu);
-       return w;
-}
-
-static void wq_sleep(struct optee_wait_queue *wq, u32 key)
-{
-       struct wq_entry *w = wq_entry_get(wq, key);
-
-       if (w) {
-               wait_for_completion(&w->c);
-               mutex_lock(&wq->mu);
-               list_del(&w->link);
-               mutex_unlock(&wq->mu);
-               kfree(w);
-       }
-}
-
-static void wq_wakeup(struct optee_wait_queue *wq, u32 key)
-{
-       struct wq_entry *w = wq_entry_get(wq, key);
-
-       if (w)
-               complete(&w->c);
-}
-
 static void handle_rpc_func_cmd_wq(struct optee *optee,
                                   struct optee_msg_arg *arg)
 {
                goto bad;
 
        switch (arg->params[0].u.value.a) {
-       case OPTEE_RPC_WAIT_QUEUE_SLEEP:
-               wq_sleep(&optee->wait_queue, arg->params[0].u.value.b);
+       case OPTEE_RPC_NOTIFICATION_WAIT:
+               if (optee_notif_wait(optee, arg->params[0].u.value.b))
+                       goto bad;
                break;
-       case OPTEE_RPC_WAIT_QUEUE_WAKEUP:
-               wq_wakeup(&optee->wait_queue, arg->params[0].u.value.b);
+       case OPTEE_RPC_NOTIFICATION_SEND:
+               if (optee_notif_send(optee, arg->params[0].u.value.b))
+                       goto bad;
                break;
        default:
                goto bad;
        case OPTEE_RPC_CMD_GET_TIME:
                handle_rpc_func_cmd_get_time(arg);
                break;
-       case OPTEE_RPC_CMD_WAIT_QUEUE:
+       case OPTEE_RPC_CMD_NOTIFICATION:
                handle_rpc_func_cmd_wq(optee, arg);
                break;
        case OPTEE_RPC_CMD_SUSPEND:
 
 
        mutex_init(&optee->call_queue.mutex);
        INIT_LIST_HEAD(&optee->call_queue.waiters);
-       optee_wait_queue_init(&optee->wait_queue);
        optee_supp_init(&optee->supp);
        optee->smc.memremaped_shm = memremaped_shm;
        optee->pool = pool;
 
+       platform_set_drvdata(pdev, optee);
+       rc = optee_notif_init(optee, OPTEE_DEFAULT_MAX_NOTIF_VALUE);
+       if (rc) {
+               optee_remove(pdev);
+               return rc;
+       }
+
        /*
         * Ensure that there are no pre-existing shm objects before enabling
         * the shm cache so that there's no chance of receiving an invalid
        if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
                pr_info("dynamic shared memory is enabled\n");
 
-       platform_set_drvdata(pdev, optee);
-
        rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES);
        if (rc) {
                optee_smc_remove(pdev);