diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
index e097e894b433..3d8f1daea32d 100644
--- a/drivers/firmware/arm_ffa/driver.c
+++ b/drivers/firmware/arm_ffa/driver.c
@@ -22,15 +22,20 @@
 #define DRIVER_NAME "ARM FF-A"
 #define pr_fmt(fmt) DRIVER_NAME ": " fmt
 
+#include <linux/acpi.h>
 #include <linux/arm_ffa.h>
 #include <linux/bitfield.h>
+#include <linux/cpuhotplug.h>
 #include <linux/device.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mm.h>
+#include <linux/of_irq.h>
 #include <linux/scatterlist.h>
 #include <linux/slab.h>
+#include <linux/smp.h>
 #include <linux/uuid.h>
 
 #include "common.h"
@@ -76,6 +81,10 @@ static inline int ffa_to_linux_errno(int errno)
 	return -EINVAL;
 }
 
+struct ffa_pcpu_irq {
+	struct ffa_drv_info *info;
+};
+
 struct ffa_drv_info {
 	u32 version;
 	u16 vm_id;
@@ -85,6 +94,12 @@ struct ffa_drv_info {
 	void *tx_buffer;
 	bool mem_ops_native;
 	bool bitmap_created;
+	unsigned int sched_recv_irq;
+	unsigned int cpuhp_state;
+	struct ffa_pcpu_irq __percpu *irq_pcpu;
+	struct workqueue_struct *notif_pcpu_wq;
+	struct work_struct irq_work;
+	bool info_get_64b;
 };
 
 static struct ffa_drv_info *drv_info;
@@ -910,9 +925,153 @@ static void ffa_setup_partitions(void)
 	kfree(pbuf);
 }
 
+/* FFA FEATURE IDs */
+#define FFA_FEAT_NOTIFICATION_PENDING_INT	(1)
+#define FFA_FEAT_SCHEDULE_RECEIVER_INT		(2)
+#define FFA_FEAT_MANAGED_EXIT_INT		(3)
+
+static irqreturn_t irq_handler(int irq, void *irq_data)
+{
+	struct ffa_pcpu_irq *pcpu = irq_data;
+	struct ffa_drv_info *info = pcpu->info;
+
+	queue_work(info->notif_pcpu_wq, &info->irq_work);
+
+	return IRQ_HANDLED;
+}
+
+static void ffa_sched_recv_irq_work_fn(struct work_struct *work)
+{
+	struct ffa_drv_info *info = container_of(work, struct ffa_drv_info,
+						 irq_work);
+
+	ffa_notification_info_get(info->info_get_64b);
+}
+
+static int ffa_sched_recv_irq_map(void)
+{
+	int ret, irq, sr_intid;
+
+	/* The returned sr_intid is assumed to be SGI donated to NS world */
+	ret = ffa_features(FFA_FEAT_SCHEDULE_RECEIVER_INT, 0, &sr_intid, NULL);
+	if (ret < 0) {
+		if (ret != -EOPNOTSUPP)
+			pr_err("Failed to retrieve scheduler Rx interrupt\n");
+		return ret;
+	}
+
+	if (acpi_disabled) {
+		struct of_phandle_args oirq = {};
+		struct device_node *gic;
+
+		/* Only GICv3 supported currently with the device tree */
+		gic = of_find_compatible_node(NULL, NULL, "arm,gic-v3");
+		if (!gic)
+			return -ENXIO;
+
+		oirq.np = gic;
+		oirq.args_count = 1;
+		oirq.args[0] = sr_intid;
+		irq = irq_create_of_mapping(&oirq);
+		of_node_put(gic);
+#ifdef CONFIG_ACPI
+	} else {
+		irq = acpi_register_gsi(NULL, sr_intid, ACPI_EDGE_SENSITIVE,
+					ACPI_ACTIVE_HIGH);
+#endif
+	}
+
+	if (irq <= 0) {
+		pr_err("Failed to create IRQ mapping!\n");
+		return -ENODATA;
+	}
+
+	return irq;
+}
+
+static void ffa_sched_recv_irq_unmap(void)
+{
+	if (drv_info->sched_recv_irq)
+		irq_dispose_mapping(drv_info->sched_recv_irq);
+}
+
+static int ffa_cpuhp_pcpu_irq_enable(unsigned int cpu)
+{
+	enable_percpu_irq(drv_info->sched_recv_irq, IRQ_TYPE_NONE);
+	return 0;
+}
+
+static int ffa_cpuhp_pcpu_irq_disable(unsigned int cpu)
+{
+	disable_percpu_irq(drv_info->sched_recv_irq);
+	return 0;
+}
+
+static void ffa_uninit_pcpu_irq(void)
+{
+	if (drv_info->cpuhp_state)
+		cpuhp_remove_state(drv_info->cpuhp_state);
+
+	if (drv_info->notif_pcpu_wq)
+		destroy_workqueue(drv_info->notif_pcpu_wq);
+
+	if (drv_info->sched_recv_irq)
+		free_percpu_irq(drv_info->sched_recv_irq, drv_info->irq_pcpu);
+
+	if (drv_info->irq_pcpu)
+		free_percpu(drv_info->irq_pcpu);
+}
+
+static int ffa_init_pcpu_irq(unsigned int irq)
+{
+	struct ffa_pcpu_irq __percpu *irq_pcpu;
+	int ret, cpu;
+
+	irq_pcpu = alloc_percpu(struct ffa_pcpu_irq);
+	if (!irq_pcpu)
+		return -ENOMEM;
+
+	for_each_present_cpu(cpu)
+		per_cpu_ptr(irq_pcpu, cpu)->info = drv_info;
+
+	drv_info->irq_pcpu = irq_pcpu;
+
+	ret = request_percpu_irq(irq, irq_handler, "ARM-FFA", irq_pcpu);
+	if (ret) {
+		pr_err("Error registering notification IRQ %d: %d\n", irq, ret);
+		return ret;
+	}
+
+	INIT_WORK(&drv_info->irq_work, ffa_sched_recv_irq_work_fn);
+	drv_info->notif_pcpu_wq = create_workqueue("ffa_pcpu_irq_notification");
+	if (!drv_info->notif_pcpu_wq)
+		return -EINVAL;
+
+	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "ffa/pcpu-irq:starting",
+				ffa_cpuhp_pcpu_irq_enable,
+				ffa_cpuhp_pcpu_irq_disable);
+
+	if (ret < 0)
+		return ret;
+
+	drv_info->cpuhp_state = ret;
+	return 0;
+}
+
+static void ffa_notifications_cleanup(void)
+{
+	ffa_uninit_pcpu_irq();
+	ffa_sched_recv_irq_unmap();
+
+	if (drv_info->bitmap_created) {
+		ffa_notification_bitmap_destroy();
+		drv_info->bitmap_created = false;
+	}
+}
+
 static int ffa_notifications_setup(void)
 {
-	int ret;
+	int ret, irq;
 
 	ret = ffa_features(FFA_NOTIFICATION_BITMAP_CREATE, 0, NULL, NULL);
 	if (!ret) {
@@ -924,15 +1083,24 @@ static int ffa_notifications_setup(void)
 	}
 	drv_info->bitmap_created = true;
 
-	return 0;
-}
+	irq = ffa_sched_recv_irq_map();
+	if (irq <= 0)
+		goto cleanup;
 
-static void ffa_notifications_cleanup(void)
-{
-	if (drv_info->bitmap_created) {
-		ffa_notification_bitmap_destroy();
-		drv_info->bitmap_created = false;
-	}
+	if (FFA_FN_NATIVE(NOTIFICATION_INFO_GET) ==
+		FFA_FN64_NOTIFICATION_INFO_GET)
+		drv_info->info_get_64b = true;
+
+	drv_info->sched_recv_irq = irq;
+
+	ret = ffa_init_pcpu_irq(irq);
+	if (ret)
+		goto cleanup;
+
+	return 0;
+cleanup:
+	ffa_notifications_cleanup();
+	return ret;
 }
 
 static int __init ffa_init(void)
@@ -990,7 +1158,11 @@ static int __init ffa_init(void)
 
 	ffa_set_up_mem_ops_native_flag();
 
-	return ffa_notifications_setup();
+	ret = ffa_notifications_setup();
+	if (ret)
+		goto free_pages;
+
+	return 0;
 free_pages:
 	if (drv_info->tx_buffer)
 		free_pages_exact(drv_info->tx_buffer, RXTX_BUFFER_SIZE);
