diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 333666e..b89acb0 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -200,6 +200,8 @@ config ST_SLIM_REMOTEPROC
 config PRUSS_REMOTEPROC
 	tristate "TI PRUSS remoteproc support"
 	depends on TI_PRUSS
+	select MAILBOX
+	select OMAP2PLUS_MBOX if ARCH_OMAP2PLUS
 	default n
 	help
 	  Support for TI PRU-ICSS remote processors via the remote processor
diff --git a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c
index 85463f7..b47c4fd 100644
--- a/drivers/remoteproc/pru_rproc.c
+++ b/drivers/remoteproc/pru_rproc.c
@@ -11,6 +11,7 @@
 #include <linux/debugfs.h>
 #include <linux/interrupt.h>
 #include <linux/irqchip/irq-pruss-intc.h>
+#include <linux/mailbox_client.h>
 #include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
@@ -64,6 +65,10 @@
  * @debug_regmap: regmap to PRU DEBUG IOMEM
  * @client_np: client device node
  * @intc_config: PRU INTC configuration data
+ * @mbox: mailbox channel handle used for vring signalling with MPU
+ * @client: mailbox client to request the mailbox channel
+ * @irq_ring: IRQ number to use for processing vring buffers
+ * @irq_kick: IRQ number to use to perform virtio kick
  * @dram0: PRUSS DRAM0 region
  * @dram1: PRUSS DRAM1 region
  * @shrdram: PRUSS SHARED RAM region
@@ -90,6 +95,10 @@ struct pru_rproc {
 	struct regmap *debug_regmap;
 	struct device_node *client_np;
 	struct pruss_intc_config intc_config;
+	struct mbox_chan *mbox;
+	struct mbox_client client;
+	int irq_vring;
+	int irq_kick;
 	struct pruss_mem_region dram0;
 	struct pruss_mem_region dram1;
 	struct pruss_mem_region shrdram;
@@ -615,6 +624,82 @@ static int pru_rproc_handle_vendor_rsc(struct rproc *rproc,
 	return ret;
 }
 
+/**
+ * pru_rproc_mbox_callback() - inbound mailbox message handler
+ * @client: mailbox client pointer used for requesting the mailbox channel
+ * @data: mailbox payload
+ *
+ * This handler is invoked by omap's mailbox driver whenever a mailbox
+ * message is received. Usually, the mailbox payload simply contains
+ * the index of the virtqueue that is kicked by the PRU remote processor,
+ * and we let remoteproc core handle it.
+ *
+ * In addition to virtqueue indices, we might also have some out-of-band
+ * values that indicates different events. Those values are deliberately
+ * very big so they don't coincide with virtqueue indices.
+ */
+static void pru_rproc_mbox_callback(struct mbox_client *client, void *data)
+{
+	struct pru_rproc *pru = container_of(client, struct pru_rproc, client);
+	struct device *dev = &pru->rproc->dev;
+	u32 msg = (u32)data;
+
+	dev_dbg(dev, "mbox msg: 0x%x\n", msg);
+
+	/* msg contains the index of the triggered vring */
+	if (rproc_vq_interrupt(pru->rproc, msg) == IRQ_NONE)
+		dev_dbg(dev, "no message was found in vqid %d\n", msg);
+}
+
+/**
+ * pru_rproc_vring_interrupt() - interrupt handler for processing vrings
+ * @irq: irq number associated with the PRU event MPU is listening on
+ * @data: interrupt handler data, will be a PRU rproc structure
+ *
+ * This handler is used by the PRU remoteproc driver when using PRU system
+ * events for processing the virtqueues. Unlike the mailbox IP, there is
+ * no payload associated with an interrupt, so either a unique event is
+ * used for each virtqueue kick, or a both virtqueues are processed on
+ * a single event. The latter is chosen to conserve the usable PRU system
+ * events.
+ */
+static irqreturn_t pru_rproc_vring_interrupt(int irq, void *data)
+{
+	struct pru_rproc *pru = data;
+
+	dev_dbg(&pru->rproc->dev, "got vring irq\n");
+
+	/* process incoming buffers on both the Rx and Tx vrings */
+	rproc_vq_interrupt(pru->rproc, 0);
+	rproc_vq_interrupt(pru->rproc, 1);
+
+	return IRQ_HANDLED;
+}
+
+/* kick a virtqueue */
+static void pru_rproc_kick(struct rproc *rproc, int vq_id)
+{
+	struct device *dev = &rproc->dev;
+	struct pru_rproc *pru = rproc->priv;
+	int ret;
+
+	dev_dbg(dev, "kicking vqid %d on PRU%d\n", vq_id, pru->id);
+
+	if (pru->irq_kick > 0) {
+		ret = pruss_intc_trigger(pru->irq_kick);
+		if (ret < 0)
+			dev_err(dev, "pruss_intc_trigger failed: %d\n", ret);
+	} else if (pru->mbox) {
+		/*
+		 * send the index of the triggered virtqueue in the mailbox
+		 * payload
+		 */
+		ret = mbox_send_message(pru->mbox, (void *)vq_id);
+		if (ret < 0)
+			dev_err(dev, "mbox_send_message failed: %d\n", ret);
+	}
+}
+
 /* start a PRU core */
 static int pru_rproc_start(struct rproc *rproc)
 {
@@ -635,10 +720,36 @@ static int pru_rproc_start(struct rproc *rproc)
 		}
 	}
 
+	if (!list_empty(&pru->rproc->rvdevs)) {
+		if (!pru->mbox && (pru->irq_vring <= 0 || pru->irq_kick <= 0)) {
+			dev_err(dev, "virtio vring interrupt mechanisms are not provided\n");
+			ret = -EINVAL;
+			goto fail;
+		}
+
+		if (!pru->mbox && pru->irq_vring > 0) {
+			ret = request_threaded_irq(pru->irq_vring, NULL,
+						   pru_rproc_vring_interrupt,
+						   IRQF_ONESHOT, dev_name(dev),
+						   pru);
+			if (ret) {
+				dev_err(dev, "failed to enable vring interrupt, ret = %d\n",
+					ret);
+				goto fail;
+			}
+		}
+	}
+
 	val = CTRL_CTRL_EN | ((rproc->bootaddr >> 2) << 16);
 	regmap_write(pru->ctrl_regmap, PRU_CTRL_CTRL, val);
 
 	return 0;
+
+fail:
+	if (pru->dt_irqs || pru->fw_irqs)
+		pruss_intc_unconfigure(rproc->dev.parent, &pru->intc_config);
+
+	return ret;
 }
 
 /* stop/disable a PRU core */
@@ -650,6 +761,10 @@ static int pru_rproc_stop(struct rproc *rproc)
 	dev_dbg(dev, "stopping PRU%d\n", pru->id);
 	regmap_update_bits(pru->ctrl_regmap, PRU_CTRL_CTRL, CTRL_CTRL_EN, 0);
 
+	if (!list_empty(&pru->rproc->rvdevs) &&
+	    !pru->mbox && pru->irq_vring > 0)
+		free_irq(pru->irq_vring, pru);
+
 	/* undo INTC config */
 	if (pru->dt_irqs || pru->fw_irqs)
 		pruss_intc_unconfigure(rproc->dev.parent, &pru->intc_config);
@@ -746,6 +861,7 @@ static void *pru_da_to_va(struct rproc *rproc, u64 da, int len, int map)
 static struct rproc_ops pru_rproc_ops = {
 	.start			= pru_rproc_start,
 	.stop			= pru_rproc_stop,
+	.kick			= pru_rproc_kick,
 	.da_to_va		= pru_da_to_va,
 	.handle_vendor_rsc	= pru_rproc_handle_vendor_rsc,
 };
@@ -789,6 +905,7 @@ static int pru_rproc_probe(struct platform_device *pdev)
 	struct pru_rproc *pru;
 	const char *fw_name;
 	struct rproc *rproc = NULL;
+	struct mbox_client *client;
 	struct resource *res;
 	int ret;
 	void __iomem *va;
@@ -924,10 +1041,51 @@ static int pru_rproc_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, rproc);
 
+	/* get optional vring and kick interrupts for supporting virtio rpmsg */
+	pru->irq_vring = platform_get_irq_byname(pdev, "vring");
+	if (pru->irq_vring <= 0) {
+		ret = pru->irq_vring;
+		if (ret == -EPROBE_DEFER)
+			goto free_rproc;
+		dev_dbg(dev, "unable to get vring interrupt, status = %d\n",
+			ret);
+	}
+
+	pru->irq_kick = platform_get_irq_byname(pdev, "kick");
+	if (pru->irq_kick <= 0) {
+		ret = pru->irq_kick;
+		if (ret == -EPROBE_DEFER)
+			goto free_rproc;
+		dev_dbg(dev, "unable to get kick interrupt, status = %d\n",
+			ret);
+	}
+
+	/*
+	 * get optional mailbox for virtio rpmsg signalling if vring and kick
+	 * interrupts are not specified for OMAP architecture based SoCs
+	 */
+	if (pru->irq_vring <= 0 && pru->irq_kick <= 0 &&
+	    !of_device_is_compatible(np, "ti,k2g-pru") &&
+	    !of_device_is_compatible(np, "ti,da850-pru")) {
+		client = &pru->client;
+		client->dev = dev;
+		client->tx_done = NULL;
+		client->rx_callback = pru_rproc_mbox_callback;
+		client->tx_block = false;
+		client->knows_txdone = false;
+		pru->mbox = mbox_request_channel(client, 0);
+		if (IS_ERR(pru->mbox)) {
+			ret = PTR_ERR(pru->mbox);
+			pru->mbox = NULL;
+			dev_dbg(dev, "unable to get mailbox channel, status = %d\n",
+				ret);
+		}
+	}
+
 	ret = rproc_add(pru->rproc);
 	if (ret) {
 		dev_err(dev, "rproc_add failed: %d\n", ret);
-		goto free_rproc;
+		goto put_mbox;
 	}
 
 	pru_rproc_create_debug_entries(rproc);
@@ -936,6 +1094,8 @@ static int pru_rproc_probe(struct platform_device *pdev)
 
 	return 0;
 
+put_mbox:
+	mbox_free_channel(pru->mbox);
 free_rproc:
 	rproc_free(rproc);
 	return ret;
@@ -945,9 +1105,12 @@ static int pru_rproc_remove(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct rproc *rproc = platform_get_drvdata(pdev);
+	struct pru_rproc *pru = rproc->priv;
 
 	dev_info(dev, "%s: removing rproc %s\n", __func__, rproc->name);
 
+	mbox_free_channel(pru->mbox);
+
 	rproc_del(rproc);
 	rproc_free(rproc);
 
