Remoteproc patches for OMAPL138 on Linux v3.8.0
authorRobert Tivy <rtivy@ti.com>
Tue, 12 Mar 2013 21:27:06 +0000 (14:27 -0700)
committerRobert Tivy <rtivy@ti.com>
Tue, 12 Mar 2013 21:27:06 +0000 (14:27 -0700)
linux/patches/3.8.0/omapl138/0001-Process-all-available-messages-in-virtqueue-callback.patch [new file with mode: 0644]
linux/patches/3.8.0/omapl138/0002-Allow-all-virtqueues-processing-in-rproc_vq_interrup.patch [new file with mode: 0644]
linux/patches/3.8.0/omapl138/0003-Add-support-for-configuring-DA8XX_REMOTEPROC.patch [new file with mode: 0644]
linux/patches/3.8.0/omapl138/0004-Add-support-to-rproc_alloc-for-a-default-firmware-na.patch [new file with mode: 0644]
linux/patches/3.8.0/omapl138/0005-Add-a-remoteproc-driver-implementation-for-OMAP-L138.patch [new file with mode: 0644]
linux/patches/3.8.0/omapl138/0006-Add-a-new-remoteproc-platform-device-for-DA8XX.patch [new file with mode: 0644]
linux/patches/3.8.0/omapl138/0007-remoteproc-platform-support.patch [new file with mode: 0644]
linux/patches/3.8.0/rpmsg-socket.patch [new file with mode: 0644]

diff --git a/linux/patches/3.8.0/omapl138/0001-Process-all-available-messages-in-virtqueue-callback.patch b/linux/patches/3.8.0/omapl138/0001-Process-all-available-messages-in-virtqueue-callback.patch
new file mode 100644 (file)
index 0000000..86b31c3
--- /dev/null
@@ -0,0 +1,154 @@
+From c93a879d9749295d8c56df7395e28fd446e6b9a7 Mon Sep 17 00:00:00 2001
+From: Robert Tivy <rtivy@ti.com>
+Date: Fri, 8 Mar 2013 10:17:04 -0800
+Subject: [PATCH v8 1/7] Process all available messages in virtqueue callback
+
+Change virtqueue callback function rpmsg_recv_done() to process all
+available messages instead of just one message.
+
+Signed-off-by: Robert Tivy <rtivy@ti.com>
+---
+ drivers/rpmsg/virtio_rpmsg_bus.c |   97 +++++++++++++++++++++++---------------
+ 1 file changed, 58 insertions(+), 39 deletions(-)
+
+diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
+index f1e3239..ae92ae7 100644
+--- a/drivers/rpmsg/virtio_rpmsg_bus.c
++++ b/drivers/rpmsg/virtio_rpmsg_bus.c
+@@ -782,7 +782,7 @@ out:
+ }
+ EXPORT_SYMBOL(rpmsg_send_offchannel_raw);
+-/* called when an rx buffer is used, and it's time to digest a message */
++/* called when an rx buffer is used, and it's time to digest a message(s) */
+ static void rpmsg_recv_done(struct virtqueue *rvq)
+ {
+       struct rpmsg_hdr *msg;
+@@ -791,6 +791,7 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
+       struct scatterlist sg;
+       struct virtproc_info *vrp = rvq->vdev->priv;
+       struct device *dev = &rvq->vdev->dev;
++      int added_buf = 0;
+       int err;
+       msg = virtqueue_get_buf(rvq, &len);
+@@ -799,60 +800,78 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
+               return;
+       }
+-      dev_dbg(dev, "From: 0x%x, To: 0x%x, Len: %d, Flags: %d, Reserved: %d\n",
++      while (msg) {
++              dev_dbg(dev, "From: 0x%x, To: 0x%x, Len: %d, Flags: %d, Reserved: %d\n",
+                                       msg->src, msg->dst, msg->len,
+                                       msg->flags, msg->reserved);
+-      print_hex_dump(KERN_DEBUG, "rpmsg_virtio RX: ", DUMP_PREFIX_NONE, 16, 1,
++#ifdef DEBUG_VERBOSE
++              print_hex_dump(KERN_DEBUG, "rpmsg_virtio RX: ",
++                                      DUMP_PREFIX_NONE, 16, 1,
+                                       msg, sizeof(*msg) + msg->len, true);
++#endif
+-      /*
+-       * We currently use fixed-sized buffers, so trivially sanitize
+-       * the reported payload length.
+-       */
+-      if (len > RPMSG_BUF_SIZE ||
+-              msg->len > (len - sizeof(struct rpmsg_hdr))) {
+-              dev_warn(dev, "inbound msg too big: (%d, %d)\n", len, msg->len);
+-              return;
+-      }
++              /*
++               * We currently use fixed-sized buffers, so trivially sanitize
++               * the reported payload length.
++               */
++              if (len > RPMSG_BUF_SIZE ||
++                      msg->len > (len - sizeof(struct rpmsg_hdr))) {
++                      dev_warn(dev, "inbound msg too big: (%d, %d)\n",
++                                      len, msg->len);
++                      break;
++              }
+-      /* use the dst addr to fetch the callback of the appropriate user */
+-      mutex_lock(&vrp->endpoints_lock);
++              /*
++               * Use the dst addr to fetch the callback of the appropriate
++               * user.
++               */
++              mutex_lock(&vrp->endpoints_lock);
+-      ept = idr_find(&vrp->endpoints, msg->dst);
++              ept = idr_find(&vrp->endpoints, msg->dst);
+-      /* let's make sure no one deallocates ept while we use it */
+-      if (ept)
+-              kref_get(&ept->refcount);
++              /* let's make sure no one deallocates ept while we use it */
++              if (ept)
++                      kref_get(&ept->refcount);
+-      mutex_unlock(&vrp->endpoints_lock);
++              mutex_unlock(&vrp->endpoints_lock);
+-      if (ept) {
+-              /* make sure ept->cb doesn't go away while we use it */
+-              mutex_lock(&ept->cb_lock);
++              if (ept) {
++                      /* make sure ept->cb doesn't go away while we use it */
++                      mutex_lock(&ept->cb_lock);
+-              if (ept->cb)
+-                      ept->cb(ept->rpdev, msg->data, msg->len, ept->priv,
+-                              msg->src);
++                      if (ept->cb)
++                              ept->cb(ept->rpdev, msg->data, msg->len,
++                                      ept->priv, msg->src);
+-              mutex_unlock(&ept->cb_lock);
++                      mutex_unlock(&ept->cb_lock);
+-              /* farewell, ept, we don't need you anymore */
+-              kref_put(&ept->refcount, __ept_release);
+-      } else
+-              dev_warn(dev, "msg received with no recepient\n");
++                      /* farewell, ept, we don't need you anymore */
++                      kref_put(&ept->refcount, __ept_release);
++              } else
++                      dev_warn(dev, "msg received with no recepient\n");
+-      /* publish the real size of the buffer */
+-      sg_init_one(&sg, msg, RPMSG_BUF_SIZE);
++              /* publish the real size of the buffer */
++              sg_init_one(&sg, msg, RPMSG_BUF_SIZE);
+-      /* add the buffer back to the remote processor's virtqueue */
+-      err = virtqueue_add_buf(vrp->rvq, &sg, 0, 1, msg, GFP_KERNEL);
+-      if (err < 0) {
+-              dev_err(dev, "failed to add a virtqueue buffer: %d\n", err);
+-              return;
++              /* add the buffer back to the remote processor's virtqueue */
++              err = virtqueue_add_buf(vrp->rvq, &sg, 0, 1, msg, GFP_KERNEL);
++              if (err < 0) {
++                      dev_err(dev, "failed to add a virtqueue buffer: %d\n",
++                                      err);
++                      break;
++              }
++
++              added_buf = 1;
++
++              msg = virtqueue_get_buf(rvq, &len);
+       }
+-      /* tell the remote processor we added another available rx buffer */
+-      virtqueue_kick(vrp->rvq);
++      if (added_buf)
++              /*
++               * Tell the remote processor we added another available rx
++               * buffer.
++               */
++              virtqueue_kick(vrp->rvq);
+ }
+ /*
+-- 
+1.7.9.4
+
diff --git a/linux/patches/3.8.0/omapl138/0002-Allow-all-virtqueues-processing-in-rproc_vq_interrup.patch b/linux/patches/3.8.0/omapl138/0002-Allow-all-virtqueues-processing-in-rproc_vq_interrup.patch
new file mode 100644 (file)
index 0000000..96553df
--- /dev/null
@@ -0,0 +1,77 @@
+From 39236bb3d4b19af05ee6cd7a60bdc8cf833e039f Mon Sep 17 00:00:00 2001
+From: Robert Tivy <rtivy@ti.com>
+Date: Fri, 8 Mar 2013 10:22:15 -0800
+Subject: [PATCH v8 2/7] Allow "all virtqueues" processing in
+ rproc_vq_interrupt()
+
+Add support to rproc_vq_interrupt() for a notifyid of -1 which will cause
+all valid virtqueues to be processed.
+
+Signed-off-by: Robert Tivy <rtivy@ti.com>
+---
+ drivers/remoteproc/remoteproc_virtio.c |   34 ++++++++++++++++++++++++++++----
+ 1 file changed, 30 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c
+index 9e198e5..07fa6cb 100644
+--- a/drivers/remoteproc/remoteproc_virtio.c
++++ b/drivers/remoteproc/remoteproc_virtio.c
+@@ -41,6 +41,21 @@ static void rproc_virtio_notify(struct virtqueue *vq)
+       rproc->ops->kick(rproc, notifyid);
+ }
++/* processing function when iterating all virtqueues */
++static int rproc_vring_interrupt(int id, void *p, void *data)
++{
++      struct rproc_vring *rvring = (struct rproc_vring *)p;
++      irqreturn_t *retp = (irqreturn_t *)data;
++
++      if (!rvring->vq)
++              return 0;
++
++      if (vring_interrupt(0, rvring->vq) == IRQ_HANDLED)
++              *retp = IRQ_HANDLED;
++
++      return 0;
++}
++
+ /**
+  * rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted
+  * @rproc: handle to the remote processor
+@@ -52,18 +67,29 @@ static void rproc_virtio_notify(struct virtqueue *vq)
+  *
+  * Returns IRQ_NONE if no message was found in the @notifyid virtqueue,
+  * and otherwise returns IRQ_HANDLED.
++ *
++ * A @notifyid value of -1 can be passed in order to signal all available
++ * virtqueues for this @rproc.  In this case this function returns IRQ_HANDLED
++ * if any virtqueue contained a message, and IRQ_NONE if none of them did.
+  */
+ irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid)
+ {
+       struct rproc_vring *rvring;
++      irqreturn_t ret = IRQ_NONE;
+       dev_dbg(&rproc->dev, "vq index %d is interrupted\n", notifyid);
+-      rvring = idr_find(&rproc->notifyids, notifyid);
+-      if (!rvring || !rvring->vq)
+-              return IRQ_NONE;
++      if (notifyid >= 0) {
++              rvring = idr_find(&rproc->notifyids, notifyid);
++              if (!rvring || !rvring->vq)
++                      return IRQ_NONE;
+-      return vring_interrupt(0, rvring->vq);
++              return vring_interrupt(0, rvring->vq);
++      } else {
++              idr_for_each(&rproc->notifyids, rproc_vring_interrupt, &ret);
++      }
++
++      return ret;
+ }
+ EXPORT_SYMBOL(rproc_vq_interrupt);
+-- 
+1.7.9.4
+
diff --git a/linux/patches/3.8.0/omapl138/0003-Add-support-for-configuring-DA8XX_REMOTEPROC.patch b/linux/patches/3.8.0/omapl138/0003-Add-support-for-configuring-DA8XX_REMOTEPROC.patch
new file mode 100644 (file)
index 0000000..0470ec4
--- /dev/null
@@ -0,0 +1,57 @@
+From 5207f0508166595bbe43d39c4b6a7a6a0e54f0c2 Mon Sep 17 00:00:00 2001
+From: Robert Tivy <rtivy@ti.com>
+Date: Fri, 8 Mar 2013 10:28:53 -0800
+Subject: [PATCH v8 3/7] ARM: davinci: Add support for configuring DA8XX_REMOTEPROC
+
+Also fix REMOTEPROC config to select FW_LOADER (instead of FW_CONFIG).
+
+Signed-off-by: Robert Tivy <rtivy@ti.com>
+---
+ drivers/remoteproc/Kconfig |   26 +++++++++++++++++++++++++-
+ 1 file changed, 25 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
+index 96ce101..21d04f1 100644
+--- a/drivers/remoteproc/Kconfig
++++ b/drivers/remoteproc/Kconfig
+@@ -5,7 +5,7 @@ config REMOTEPROC
+       tristate
+       depends on EXPERIMENTAL
+       depends on HAS_DMA
+-      select FW_CONFIG
++      select FW_LOADER
+       select VIRTIO
+ config OMAP_REMOTEPROC
+@@ -41,4 +41,28 @@ config STE_MODEM_RPROC
+         This can be either built-in or a loadable module.
+         If unsure say N.
++config DA8XX_REMOTEPROC
++      tristate "DA830/OMAPL137 & DA850/OMAPL138 remoteproc support (EXPERIMENTAL)"
++      depends on ARCH_DAVINCI_DA8XX
++      select REMOTEPROC
++      select RPMSG
++      select CMA
++      default n
++      help
++        Say y here to support DA830/OMAPL137 & DA850/OMAPL138 remote
++        processors via the remote processor framework.
++
++        You want to say y here in order to enable AMP
++        use-cases to run on your platform (multimedia codecs are
++        offloaded to remote DSP processors using this framework).
++
++        This module controls the name of the firmware file that gets
++        loaded on the DSP.  This file must reside in the /lib/firmware
++        directory.  It can be specified via the module parameter
++        da8xx_fw_name=<filename>, and if not specified will default to
++        "rproc-dsp-fw".
++
++        It's safe to say n here if you're not interested in multimedia
++        offloading or just want a bare minimum kernel.
++
+ endmenu
+-- 
+1.7.9.4
+
diff --git a/linux/patches/3.8.0/omapl138/0004-Add-support-to-rproc_alloc-for-a-default-firmware-na.patch b/linux/patches/3.8.0/omapl138/0004-Add-support-to-rproc_alloc-for-a-default-firmware-na.patch
new file mode 100644 (file)
index 0000000..dc09e47
--- /dev/null
@@ -0,0 +1,61 @@
+From e974894771cfb415d1d2ed3d67c5a2677df8e54d Mon Sep 17 00:00:00 2001
+From: Robert Tivy <rtivy@ti.com>
+Date: Fri, 8 Mar 2013 10:33:39 -0800
+Subject: [PATCH v8 4/7] Add support to rproc_alloc() for a default firmware
+ name
+
+Signed-off-by: Robert Tivy <rtivy@ti.com>
+---
+ drivers/remoteproc/remoteproc_core.c |   25 +++++++++++++++++++++++--
+ 1 file changed, 23 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
+index dd3bfaf..94c9b74 100644
+--- a/drivers/remoteproc/remoteproc_core.c
++++ b/drivers/remoteproc/remoteproc_core.c
+@@ -1222,19 +1222,40 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
+                               const char *firmware, int len)
+ {
+       struct rproc *rproc;
++      char *template = "rproc-%s-fw";
++      char *p;
++      int name_len = 0;
+       if (!dev || !name || !ops)
+               return NULL;
+-      rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL);
++      if (!firmware)
++              /*
++               * Make room for default firmware name (minus %s plus '\0').
++               * If the caller didn't pass in a firmware name then
++               * construct a default name.  We're already glomming 'len'
++               * bytes onto the end of the struct rproc allocation, so do
++               * a few more for the default firmware name (but only if
++               * the caller doesn't pass one).
++               */
++              name_len = strlen(name) + strlen(template) - 2 + 1;
++
++      rproc = kzalloc(sizeof(struct rproc) + len + name_len, GFP_KERNEL);
+       if (!rproc) {
+               dev_err(dev, "%s: kzalloc failed\n", __func__);
+               return NULL;
+       }
++      if (!firmware) {
++              p = (char *)rproc + sizeof(struct rproc) + len;
++              sprintf(p, template, name);
++      } else {
++              p = (char *)firmware;
++      }
++
++      rproc->firmware = p;
+       rproc->name = name;
+       rproc->ops = ops;
+-      rproc->firmware = firmware;
+       rproc->priv = &rproc[1];
+       device_initialize(&rproc->dev);
+-- 
+1.7.9.4
+
diff --git a/linux/patches/3.8.0/omapl138/0005-Add-a-remoteproc-driver-implementation-for-OMAP-L138.patch b/linux/patches/3.8.0/omapl138/0005-Add-a-remoteproc-driver-implementation-for-OMAP-L138.patch
new file mode 100644 (file)
index 0000000..121040f
--- /dev/null
@@ -0,0 +1,353 @@
+From 8425d8e1cb44c188816ef6648d4c2f4fa115dcc8 Mon Sep 17 00:00:00 2001
+From: Robert Tivy <rtivy@ti.com>
+Date: Fri, 8 Mar 2013 10:36:19 -0800
+Subject: [PATCH v8 5/7] ARM: davinci: Add a remoteproc driver implementation for OMAP-L138 DSP
+
+Signed-off-by: Robert Tivy <rtivy@ti.com>
+---
+ drivers/remoteproc/Makefile           |    1 +
+ drivers/remoteproc/da8xx_remoteproc.c |  323 +++++++++++++++++++++++++++++++++
+ 2 files changed, 324 insertions(+)
+ create mode 100644 drivers/remoteproc/da8xx_remoteproc.c
+
+diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
+index 391b651..ac2ff75 100644
+--- a/drivers/remoteproc/Makefile
++++ b/drivers/remoteproc/Makefile
+@@ -9,3 +9,4 @@ remoteproc-y                           += remoteproc_virtio.o
+ remoteproc-y                          += remoteproc_elf_loader.o
+ obj-$(CONFIG_OMAP_REMOTEPROC)         += omap_remoteproc.o
+ obj-$(CONFIG_STE_MODEM_RPROC)         += ste_modem_rproc.o
++obj-$(CONFIG_DA8XX_REMOTEPROC)                += da8xx_remoteproc.o
+diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c
+new file mode 100644
+index 0000000..a40e72a
+--- /dev/null
++++ b/drivers/remoteproc/da8xx_remoteproc.c
+@@ -0,0 +1,323 @@
++/*
++ * Remote processor machine-specific module for DA8XX
++ *
++ * Copyright (C) 2013 Texas Instruments, Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ */
++
++#include <linux/bitops.h>
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/remoteproc.h>
++
++#include <mach/clock.h>   /* for davinci_clk_reset_assert/deassert() */
++
++#include "remoteproc_internal.h"
++
++static char *da8xx_fw_name;
++module_param(da8xx_fw_name, charp, S_IRUGO);
++MODULE_PARM_DESC(da8xx_fw_name,
++      "\n\t\tName of DSP firmware file in /lib/firmware");
++
++/*
++ * OMAP-L138 Technical References:
++ * http://www.ti.com/product/omap-l138
++ */
++#define SYSCFG_CHIPSIG0 BIT(0)
++#define SYSCFG_CHIPSIG1 BIT(1)
++#define SYSCFG_CHIPSIG2 BIT(2)
++#define SYSCFG_CHIPSIG3 BIT(3)
++#define SYSCFG_CHIPSIG4 BIT(4)
++
++/**
++ * struct da8xx_rproc - da8xx remote processor instance state
++ * @rproc: rproc handle
++ * @dsp_clk: placeholder for platform's DSP clk
++ * @ack_fxn: chip-specific ack function for ack'ing irq
++ * @irq_data: ack_fxn function parameter
++ * @chipsig: virt ptr to DSP interrupt registers (CHIPSIG & CHIPSIG_CLR)
++ * @bootreg: virt ptr to DSP boot address register (HOST1CFG)
++ * @irq: irq # used by this instance
++ */
++struct da8xx_rproc {
++      struct rproc *rproc;
++      struct clk *dsp_clk;
++      void (*ack_fxn)(struct irq_data *data);
++      struct irq_data *irq_data;
++      void __iomem *chipsig;
++      void __iomem *bootreg;
++      int irq;
++};
++
++/**
++ * handle_event() - inbound virtqueue message workqueue function
++ *
++ * This function is registered as a kernel thread and is scheduled by the
++ * kernel handler.
++ */
++static irqreturn_t handle_event(int irq, void *p)
++{
++      struct rproc *rproc = (struct rproc *)p;
++
++      /* Process incoming buffers on all our vrings */
++      rproc_vq_interrupt(rproc, -1);
++
++      return IRQ_HANDLED;
++}
++
++/**
++ * da8xx_rproc_callback() - inbound virtqueue message handler
++ *
++ * This handler is invoked directly by the kernel whenever the remote
++ * core (DSP) has modified the state of a virtqueue.  There is no
++ * "payload" message indicating the virtqueue index as is the case with
++ * mailbox-based implementations on OMAP4.  As such, this handler "polls"
++ * each known virtqueue index for every invocation.
++ */
++static irqreturn_t da8xx_rproc_callback(int irq, void *p)
++{
++      struct rproc *rproc = (struct rproc *)p;
++      struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
++      u32 chipsig;
++
++      chipsig = readl(drproc->chipsig);
++      if (chipsig & SYSCFG_CHIPSIG0) {
++              /* Clear interrupt level source */
++              writel(SYSCFG_CHIPSIG0, drproc->chipsig + 4);
++
++              /*
++               * ACK intr to AINTC.
++               *
++               * It has already been ack'ed by the kernel before calling
++               * this function, but since the ARM<->DSP interrupts in the
++               * CHIPSIG register are "level" instead of "pulse" variety,
++               * we need to ack it after taking down the level else we'll
++               * be called again immediately after returning.
++               */
++              drproc->ack_fxn(drproc->irq_data);
++
++              return IRQ_WAKE_THREAD;
++      }
++
++      return IRQ_HANDLED;
++}
++
++static int da8xx_rproc_start(struct rproc *rproc)
++{
++      struct device *dev = rproc->dev.parent;
++      struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
++      struct clk *dsp_clk = drproc->dsp_clk;
++
++      /* hw requires the start (boot) address be on 1KB boundary */
++      if (rproc->bootaddr & 0x3ff) {
++              dev_err(dev, "invalid boot address: must be aligned to 1KB\n");
++
++              return -EINVAL;
++      }
++      writel(rproc->bootaddr, drproc->bootreg);
++
++      clk_enable(dsp_clk);
++      davinci_clk_reset_deassert(dsp_clk);
++
++      return 0;
++}
++
++static int da8xx_rproc_stop(struct rproc *rproc)
++{
++      struct da8xx_rproc *drproc = rproc->priv;
++
++      clk_disable(drproc->dsp_clk);
++
++      return 0;
++}
++
++/* kick a virtqueue */
++static void da8xx_rproc_kick(struct rproc *rproc, int vqid)
++{
++      struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
++
++      /* Interupt remote proc */
++      writel(SYSCFG_CHIPSIG2, drproc->chipsig);
++}
++
++static struct rproc_ops da8xx_rproc_ops = {
++      .start = da8xx_rproc_start,
++      .stop = da8xx_rproc_stop,
++      .kick = da8xx_rproc_kick,
++};
++
++static int reset_assert(struct device *dev)
++{
++      struct clk *dsp_clk;
++
++      dsp_clk = clk_get(dev, NULL);
++      if (IS_ERR(dsp_clk)) {
++              dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
++              return PTR_RET(dsp_clk);
++      }
++      davinci_clk_reset_assert(dsp_clk);
++      clk_put(dsp_clk);
++
++      return 0;
++}
++
++static int da8xx_rproc_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct da8xx_rproc *drproc;
++      struct rproc *rproc;
++      struct irq_data *irq_data;
++      struct resource *bootreg_res;
++      struct resource *chipsig_res;
++      struct clk *dsp_clk;
++      void __iomem *chipsig;
++      void __iomem *bootreg;
++      int irq;
++      int ret;
++
++      irq = platform_get_irq(pdev, 0);
++      if (irq < 0) {
++              dev_err(dev, "platform_get_irq(pdev, 0) error: %d\n", irq);
++              return irq;
++      }
++
++      irq_data = irq_get_irq_data(irq);
++      if (!irq_data) {
++              dev_err(dev, "irq_get_irq_data(%d): NULL\n", irq);
++              return -EINVAL;
++      }
++
++      bootreg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      if (!bootreg_res) {
++              dev_err(dev,
++                      "platform_get_resource(IORESOURCE_MEM, 0): NULL\n");
++              return -EADDRNOTAVAIL;
++      }
++
++      chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++      if (!chipsig_res) {
++              dev_err(dev,
++                      "platform_get_resource(IORESOURCE_MEM, 1): NULL\n");
++              return -EADDRNOTAVAIL;
++      }
++
++      bootreg = devm_request_and_ioremap(dev, bootreg_res);
++      if (!bootreg) {
++              dev_err(dev, "unable to map boot register\n");
++              return -EADDRNOTAVAIL;
++      }
++
++      chipsig = devm_request_and_ioremap(dev, chipsig_res);
++      if (!chipsig) {
++              dev_err(dev, "unable to map CHIPSIG register\n");
++              return -EADDRNOTAVAIL;
++      }
++
++      dsp_clk = devm_clk_get(dev, NULL);
++      if (IS_ERR(dsp_clk)) {
++              dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
++
++              return PTR_RET(dsp_clk);
++      }
++
++      rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name,
++              sizeof(*drproc));
++      if (!rproc)
++              return -ENOMEM;
++
++      drproc = rproc->priv;
++      drproc->rproc = rproc;
++
++      platform_set_drvdata(pdev, rproc);
++
++      /* everything the ISR needs is now setup, so hook it up */
++      ret = devm_request_threaded_irq(dev, irq, da8xx_rproc_callback,
++              handle_event, 0, "da8xx-remoteproc", rproc);
++      if (ret) {
++              dev_err(dev, "devm_request_threaded_irq error: %d\n", ret);
++              goto free_rproc;
++      }
++
++      /*
++       * rproc_add() can end up enabling the DSP's clk with the DSP
++       * *not* in reset, but da8xx_rproc_start() needs the DSP to be
++       * held in reset at the time it is called.
++       */
++      ret = reset_assert(dev);
++      if (ret)
++              goto free_rproc;
++
++      ret = rproc_add(rproc);
++      if (ret) {
++              dev_err(dev, "rproc_add failed: %d\n", ret);
++              goto free_rproc;
++      }
++
++      drproc->chipsig = chipsig;
++      drproc->bootreg = bootreg;
++      drproc->ack_fxn = irq_data->chip->irq_ack;
++      drproc->irq_data = irq_data;
++      drproc->irq = irq;
++      drproc->dsp_clk = dsp_clk;
++
++      return 0;
++
++free_rproc:
++      rproc_put(rproc);
++
++      return ret;
++}
++
++static int da8xx_rproc_remove(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct rproc *rproc = platform_get_drvdata(pdev);
++      struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
++
++      /*
++       * It's important to place the DSP in reset before going away,
++       * since a subsequent insmod of this module may enable the DSP's
++       * clock before its program/boot-address has been loaded and
++       * before this module's probe has had a chance to reset the DSP.
++       * Without the reset, the DSP can lockup permanently when it
++       * begins executing garbage.
++       */
++      reset_assert(dev);
++
++      /*
++       * The devm subsystem might end up releasing things before
++       * freeing the irq, thus allowing an interrupt to sneak in while
++       * the device is being removed.  This should prevent that.
++       */
++      disable_irq(drproc->irq);
++
++      devm_clk_put(dev, drproc->dsp_clk);
++
++      rproc_del(rproc);
++      rproc_put(rproc);
++
++      return 0;
++}
++
++static struct platform_driver da8xx_rproc_driver = {
++      .probe = da8xx_rproc_probe,
++      .remove = da8xx_rproc_remove,
++      .driver = {
++              .name = "davinci-rproc",
++              .owner = THIS_MODULE,
++      },
++};
++
++module_platform_driver(da8xx_rproc_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("DA8XX Remote Processor control driver");
+-- 
+1.7.9.4
+
diff --git a/linux/patches/3.8.0/omapl138/0006-Add-a-new-remoteproc-platform-device-for-DA8XX.patch b/linux/patches/3.8.0/omapl138/0006-Add-a-new-remoteproc-platform-device-for-DA8XX.patch
new file mode 100644 (file)
index 0000000..cd14c05
--- /dev/null
@@ -0,0 +1,172 @@
+From 81117b56046ed3dc5540eca2b7782fd5a6e17a34 Mon Sep 17 00:00:00 2001
+From: Robert Tivy <rtivy@ti.com>
+Date: Fri, 8 Mar 2013 10:55:18 -0800
+Subject: [PATCH v8 6/7] ARM: davinci: Add a new remoteproc platform device for DA8XX
+
+Contains CMA-based reservation of physical memory block. A new kernel
+command-line parameter has been added to allow boot-time specification
+of the physical memory block.
+
+Signed-off-by: Robert Tivy <rtivy@ti.com>
+Acked-by: Sekhar Nori <nsekhar@ti.com>
+---
+ Documentation/kernel-parameters.txt        |    6 ++
+ arch/arm/mach-davinci/devices-da8xx.c      |   88 +++++++++++++++++++++++++++-
+ arch/arm/mach-davinci/include/mach/da8xx.h |    4 ++
+ 3 files changed, 97 insertions(+), 1 deletion(-)
+
+diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
+index 6c72381..a46456a 100644
+--- a/Documentation/kernel-parameters.txt
++++ b/Documentation/kernel-parameters.txt
+@@ -44,6 +44,7 @@ parameter is applicable:
+       AVR32   AVR32 architecture is enabled.
+       AX25    Appropriate AX.25 support is enabled.
+       BLACKFIN Blackfin architecture is enabled.
++      CMA     Contiguous Memory Area support is enabled.
+       DRM     Direct Rendering Management support is enabled.
+       DYNAMIC_DEBUG Build in debug messages and enable them at runtime
+       EDD     BIOS Enhanced Disk Drive Services (EDD) is enabled
+@@ -2634,6 +2635,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
+                       Useful for devices that are detected asynchronously
+                       (e.g. USB and MMC devices).
++      rproc_mem=nn[KMG][@address]
++                      [KNL,ARM,CMA] Remoteproc physical memory block.
++                      Memory area to be used by remote processor image,
++                      managed by CMA.
++
+       rw              [KNL] Mount root device read-write on boot
+       S               [KNL] Run init in single mode
+diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c
+index fc50243..08a6b36 100644
+--- a/arch/arm/mach-davinci/devices-da8xx.c
++++ b/arch/arm/mach-davinci/devices-da8xx.c
+@@ -12,7 +12,7 @@
+  */
+ #include <linux/init.h>
+ #include <linux/platform_device.h>
+-#include <linux/dma-mapping.h>
++#include <linux/dma-contiguous.h>
+ #include <linux/serial_8250.h>
+ #include <linux/ahci_platform.h>
+ #include <linux/clk.h>
+@@ -714,6 +714,92 @@ int __init da850_register_mmcsd1(struct davinci_mmc_config *config)
+ }
+ #endif
++static struct resource da8xx_rproc_resources[] = {
++      { /* DSP boot address */
++              .start          = DA8XX_SYSCFG0_BASE + DA8XX_HOST1CFG_REG,
++              .end            = DA8XX_SYSCFG0_BASE + DA8XX_HOST1CFG_REG + 3,
++              .flags          = IORESOURCE_MEM,
++      },
++      { /* DSP interrupt registers */
++              .start          = DA8XX_SYSCFG0_BASE + DA8XX_CHIPSIG_REG,
++              .end            = DA8XX_SYSCFG0_BASE + DA8XX_CHIPSIG_REG + 7,
++              .flags          = IORESOURCE_MEM,
++      },
++      { /* dsp irq */
++              .start          = IRQ_DA8XX_CHIPINT0,
++              .end            = IRQ_DA8XX_CHIPINT0,
++              .flags          = IORESOURCE_IRQ,
++      },
++};
++
++static struct platform_device da8xx_dsp = {
++      .name   = "davinci-rproc",
++      .dev    = {
++              .coherent_dma_mask      = DMA_BIT_MASK(32),
++      },
++      .num_resources  = ARRAY_SIZE(da8xx_rproc_resources),
++      .resource       = da8xx_rproc_resources,
++};
++
++#if IS_ENABLED(CONFIG_DA8XX_REMOTEPROC)
++
++static phys_addr_t rproc_base __initdata;
++static unsigned long rproc_size __initdata;
++
++static int __init early_rproc_mem(char *p)
++{
++      char *endp;
++
++      if (p == NULL)
++              return 0;
++
++      rproc_size = memparse(p, &endp);
++      if (*endp == '@')
++              rproc_base = memparse(endp + 1, NULL);
++
++      return 0;
++}
++early_param("rproc_mem", early_rproc_mem);
++
++void __init da8xx_rproc_reserve_cma(void)
++{
++      int ret;
++
++      if (!rproc_base || !rproc_size) {
++              pr_err("%s: 'rproc_mem=nn@address' badly specified\n"
++                     "    'nn' and 'address' must both be non-zero\n",
++                     __func__);
++
++              return;
++      }
++
++      pr_info("%s: reserving 0x%lx @ 0x%lx...\n",
++              __func__, rproc_size, (unsigned long)rproc_base);
++
++      ret = dma_declare_contiguous(&da8xx_dsp.dev, rproc_size, rproc_base, 0);
++      if (ret)
++              pr_err("%s: dma_declare_contiguous failed %d\n", __func__, ret);
++}
++
++#else
++
++void __init da8xx_rproc_reserve_cma(void)
++{
++}
++
++#endif
++
++int __init da8xx_register_rproc(void)
++{
++      int ret;
++
++      ret = platform_device_register(&da8xx_dsp);
++      if (ret)
++              pr_err("%s: can't register DSP device: %d\n", __func__, ret);
++
++      return ret;
++};
++
+ static struct resource da8xx_rtc_resources[] = {
+       {
+               .start          = DA8XX_RTC_BASE,
+diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h
+index de439b7..5e85c27 100644
+--- a/arch/arm/mach-davinci/include/mach/da8xx.h
++++ b/arch/arm/mach-davinci/include/mach/da8xx.h
+@@ -54,6 +54,8 @@ extern unsigned int da850_max_speed;
+ #define DA8XX_SYSCFG0_BASE    (IO_PHYS + 0x14000)
+ #define DA8XX_SYSCFG0_VIRT(x) (da8xx_syscfg0_base + (x))
+ #define DA8XX_JTAG_ID_REG     0x18
++#define DA8XX_HOST1CFG_REG    0x44
++#define DA8XX_CHIPSIG_REG     0x174
+ #define DA8XX_CFGCHIP0_REG    0x17c
+ #define DA8XX_CFGCHIP2_REG    0x184
+ #define DA8XX_CFGCHIP3_REG    0x188
+@@ -104,6 +106,8 @@ int __init da850_register_vpif_display
+ int __init da850_register_vpif_capture
+                       (struct vpif_capture_config *capture_config);
+ void da8xx_restart(char mode, const char *cmd);
++void da8xx_rproc_reserve_cma(void);
++int da8xx_register_rproc(void);
+ extern struct platform_device da8xx_serial_device;
+ extern struct emac_platform_data da8xx_emac_pdata;
+-- 
+1.7.9.4
+
diff --git a/linux/patches/3.8.0/omapl138/0007-remoteproc-platform-support.patch b/linux/patches/3.8.0/omapl138/0007-remoteproc-platform-support.patch
new file mode 100644 (file)
index 0000000..e924777
--- /dev/null
@@ -0,0 +1,62 @@
+From 527c0f1f2bcd142770804685987650624d9414d9 Mon Sep 17 00:00:00 2001
+From: Robert Tivy <rtivy@ti.com>
+Date: Fri, 8 Mar 2013 10:59:00 -0800
+Subject: [PATCH v8 7/7] ARM: davinci: da850 board: remoteproc platform support
+
+Add .reserve function for reserving CMA memory block to MACHINE_START.
+
+Add call to remoteproc platform device registration function during init.
+
+Signed-off-by: Robert Tivy <rtivy@ti.com>
+---
+ arch/arm/mach-davinci/board-da850-evm.c     |    6 ++++++
+ arch/arm/mach-davinci/board-omapl138-hawk.c |    6 ++++++
+ 2 files changed, 12 insertions(+)
+
+diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c
+index bbb7979..3277bcf 100644
+--- a/arch/arm/mach-davinci/board-da850-evm.c
++++ b/arch/arm/mach-davinci/board-da850-evm.c
+@@ -1566,6 +1566,11 @@ static __init void da850_evm_init(void)
+               pr_warn("%s: SATA registration failed: %d\n", __func__, ret);
+       da850_evm_setup_mac_addr();
++
++      ret = da8xx_register_rproc();
++      if (ret)
++              pr_warn("%s: dsp/rproc registration failed: %d\n",
++                      __func__, ret);
+ }
+ #ifdef CONFIG_SERIAL_8250_CONSOLE
+@@ -1593,4 +1598,5 @@ MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM")
+       .init_late      = davinci_init_late,
+       .dma_zone_size  = SZ_128M,
+       .restart        = da8xx_restart,
++      .reserve        = da8xx_rproc_reserve_cma,
+ MACHINE_END
+diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c
+index d5a6bcd..f60cfd1 100644
+--- a/arch/arm/mach-davinci/board-omapl138-hawk.c
++++ b/arch/arm/mach-davinci/board-omapl138-hawk.c
+@@ -311,6 +311,11 @@ static __init void omapl138_hawk_init(void)
+       if (ret)
+               pr_warn("%s: watchdog registration failed: %d\n",
+                       __func__, ret);
++
++      ret = da8xx_register_rproc();
++      if (ret)
++              pr_warn("%s: dsp/rproc registration failed: %d\n",
++                      __func__, ret);
+ }
+ #ifdef CONFIG_SERIAL_8250_CONSOLE
+@@ -338,4 +343,5 @@ MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard")
+       .init_late      = davinci_init_late,
+       .dma_zone_size  = SZ_128M,
+       .restart        = da8xx_restart,
++      .reserve        = da8xx_rproc_reserve_cma,
+ MACHINE_END
+-- 
+1.7.9.4
+
diff --git a/linux/patches/3.8.0/rpmsg-socket.patch b/linux/patches/3.8.0/rpmsg-socket.patch
new file mode 100644 (file)
index 0000000..93c6555
--- /dev/null
@@ -0,0 +1,982 @@
+From 86df5badf5430ee676d662a80e6179f4e8a082e3 Mon Sep 17 00:00:00 2001
+From: Robert Tivy <rtivy@ti.com>
+Date: Mon, 11 Mar 2013 17:01:23 -0700
+Subject: [PATCH] Add new RPMSG socket support
+
+---
+ drivers/rpmsg/virtio_rpmsg_bus.c |   87 +++++-
+ include/linux/rpmsg.h            |    4 +
+ include/linux/socket.h           |    5 +-
+ include/net/rpmsg.h              |   58 ++++
+ net/Makefile                     |    1 +
+ net/rpmsg/Makefile               |    1 +
+ net/rpmsg/rpmsg_proto.c          |  623 ++++++++++++++++++++++++++++++++++++++
+ 7 files changed, 769 insertions(+), 10 deletions(-)
+ create mode 100644 include/net/rpmsg.h
+ create mode 100644 net/rpmsg/Makefile
+ create mode 100644 net/rpmsg/rpmsg_proto.c
+
+diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
+index ae92ae7..9e2f034 100644
+--- a/drivers/rpmsg/virtio_rpmsg_bus.c
++++ b/drivers/rpmsg/virtio_rpmsg_bus.c
+@@ -51,6 +51,7 @@
+  * @sendq:    wait queue of sending contexts waiting for a tx buffers
+  * @sleepers: number of senders that are waiting for a tx buffer
+  * @ns_ept:   the bus's name service endpoint
++ * @id:               unique system-wide index id for this vproc
+  *
+  * This structure stores the rpmsg state of a given virtio remote processor
+  * device (there might be several virtio proc devices for each physical
+@@ -68,8 +69,15 @@ struct virtproc_info {
+       wait_queue_head_t sendq;
+       atomic_t sleepers;
+       struct rpmsg_endpoint *ns_ept;
++      int id;
+ };
++int get_virtproc_id(struct virtproc_info *vrp)
++{
++      return vrp->id;
++}
++EXPORT_SYMBOL(get_virtproc_id);
++
+ /**
+  * struct rpmsg_channel_info - internal channel info representation
+  * @name: name of service
+@@ -134,6 +142,16 @@ rpmsg_show_attr(dst, dst, "0x%x\n");
+ rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n");
+ /*
++ * The virtio devices we are probed with represent remote processors
++ * on the system. We call them _virtual_ processors, because every physical
++ * remote processor might actually have several virtio devices.
++ *
++ * The idr below is used to assign each vproc a unique, system-wide, index id.
++ */
++static struct idr vprocs;
++static DEFINE_MUTEX(vprocs_mutex);
++
++/*
+  * Unique (and free running) index for rpmsg devices.
+  *
+  * Yeah, we're not recycling those numbers (yet?). will be easy
+@@ -499,7 +517,7 @@ static int rpmsg_channel_match(struct device *dev, void *data)
+  * this function will be used to create both static and dynamic
+  * channels.
+  */
+-static struct rpmsg_channel *rpmsg_create_channel(struct virtproc_info *vrp,
++static struct rpmsg_channel *__rpmsg_create_channel(struct virtproc_info *vrp,
+                               struct rpmsg_channel_info *chinfo)
+ {
+       struct rpmsg_channel *rpdev;
+@@ -555,7 +573,7 @@ static struct rpmsg_channel *rpmsg_create_channel(struct virtproc_info *vrp,
+  * find an existing channel using its name + address properties,
+  * and destroy it
+  */
+-static int rpmsg_destroy_channel(struct virtproc_info *vrp,
++static int __rpmsg_destroy_channel(struct virtproc_info *vrp,
+                                       struct rpmsg_channel_info *chinfo)
+ {
+       struct virtio_device *vdev = vrp->vdev;
+@@ -755,8 +773,10 @@ int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst,
+       dev_dbg(dev, "TX From 0x%x, To 0x%x, Len %d, Flags %d, Reserved %d\n",
+                                       msg->src, msg->dst, msg->len,
+                                       msg->flags, msg->reserved);
++#ifdef DEBUG_VERBOSE
+       print_hex_dump(KERN_DEBUG, "rpmsg_virtio TX: ", DUMP_PREFIX_NONE, 16, 1,
+                                       msg, sizeof(*msg) + msg->len, true);
++#endif
+       sg_init_one(&sg, msg, sizeof(*msg) + len);
+@@ -891,6 +911,25 @@ static void rpmsg_xmit_done(struct virtqueue *svq)
+       wake_up_interruptible(&vrp->sendq);
+ }
++struct rpmsg_channel *rpmsg_create_channel(int vrp_id, const char *name,
++                                                      int src, int dst)
++{
++      struct rpmsg_channel_info chinfo;
++      struct virtproc_info *vrp;
++
++      strncpy(chinfo.name, name, sizeof(chinfo.name));
++      chinfo.src = src;
++      chinfo.dst = dst;
++
++      /* TODO we probably want radix tree and fw-induced id numbers ? */
++      vrp = idr_find(&vprocs, vrp_id);
++      if (!vrp)
++              return NULL;
++
++      return __rpmsg_create_channel(vrp, &chinfo);
++}
++EXPORT_SYMBOL(rpmsg_create_channel);
++
+ /* invoked when a name service announcement arrives */
+ static void rpmsg_ns_cb(struct rpmsg_channel *rpdev, void *data, int len,
+                                                       void *priv, u32 src)
+@@ -902,10 +941,11 @@ static void rpmsg_ns_cb(struct rpmsg_channel *rpdev, void *data, int len,
+       struct device *dev = &vrp->vdev->dev;
+       int ret;
++#ifdef DEBUG_VERBOSE
+       print_hex_dump(KERN_DEBUG, "NS announcement: ",
+                       DUMP_PREFIX_NONE, 16, 1,
+                       data, len, true);
+-
++#endif
+       if (len != sizeof(*msg)) {
+               dev_err(dev, "malformed ns msg (%d)\n", len);
+               return;
+@@ -934,13 +974,13 @@ static void rpmsg_ns_cb(struct rpmsg_channel *rpdev, void *data, int len,
+       chinfo.dst = msg->addr;
+       if (msg->flags & RPMSG_NS_DESTROY) {
+-              ret = rpmsg_destroy_channel(vrp, &chinfo);
++              ret = __rpmsg_destroy_channel(vrp, &chinfo);
+               if (ret)
+-                      dev_err(dev, "rpmsg_destroy_channel failed: %d\n", ret);
++                      dev_err(dev, "__rpmsg_destroy_channel err: %d\n", ret);
+       } else {
+-              newch = rpmsg_create_channel(vrp, &chinfo);
++              newch = __rpmsg_create_channel(vrp, &chinfo);
+               if (!newch)
+-                      dev_err(dev, "rpmsg_create_channel failed\n");
++                      dev_err(dev, "__rpmsg_create_channel failed\n");
+       }
+ }
+@@ -951,7 +991,7 @@ static int rpmsg_probe(struct virtio_device *vdev)
+       struct virtqueue *vqs[2];
+       struct virtproc_info *vrp;
+       void *bufs_va;
+-      int err = 0, i;
++      int err = 0, i, vproc_id;
+       vrp = kzalloc(sizeof(*vrp), GFP_KERNEL);
+       if (!vrp)
+@@ -964,10 +1004,26 @@ static int rpmsg_probe(struct virtio_device *vdev)
+       mutex_init(&vrp->tx_lock);
+       init_waitqueue_head(&vrp->sendq);
++      if (!idr_pre_get(&vprocs, GFP_KERNEL))
++              goto free_vrp;
++
++      mutex_lock(&vprocs_mutex);
++
++      err = idr_get_new(&vprocs, vrp, &vproc_id);
++
++      mutex_unlock(&vprocs_mutex);
++
++      if (err) {
++              dev_err(&vdev->dev, "idr_get_new failed: %d\n", err);
++              goto free_vrp;
++      }
++
++      vrp->id = vproc_id;
++
+       /* We expect two virtqueues, rx and tx (and in this order) */
+       err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, names);
+       if (err)
+-              goto free_vrp;
++              goto rem_idr;
+       vrp->rvq = vqs[0];
+       vrp->svq = vqs[1];
+@@ -1029,6 +1085,10 @@ free_coherent:
+                                       bufs_va, vrp->bufs_dma);
+ vqs_del:
+       vdev->config->del_vqs(vrp->vdev);
++rem_idr:
++      mutex_lock(&vprocs_mutex);
++      idr_remove(&vprocs, vproc_id);
++      mutex_unlock(&vprocs_mutex);
+ free_vrp:
+       kfree(vrp);
+       return err;
+@@ -1063,6 +1123,10 @@ static void rpmsg_remove(struct virtio_device *vdev)
+       dma_free_coherent(vdev->dev.parent->parent, RPMSG_TOTAL_BUF_SPACE,
+                                       vrp->rbufs, vrp->bufs_dma);
++      mutex_lock(&vprocs_mutex);
++      idr_remove(&vprocs, vrp->id);
++      mutex_unlock(&vprocs_mutex);
++
+       kfree(vrp);
+ }
+@@ -1089,6 +1153,8 @@ static int __init rpmsg_init(void)
+ {
+       int ret;
++      idr_init(&vprocs);
++
+       ret = bus_register(&rpmsg_bus);
+       if (ret) {
+               pr_err("failed to register rpmsg bus: %d\n", ret);
+@@ -1109,6 +1175,9 @@ static void __exit rpmsg_fini(void)
+ {
+       unregister_virtio_driver(&virtio_ipc_driver);
+       bus_unregister(&rpmsg_bus);
++
++      idr_remove_all(&vprocs);
++      idr_destroy(&vprocs);
+ }
+ module_exit(rpmsg_fini);
+diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h
+index 82a6739..05d0bc8 100644
+--- a/include/linux/rpmsg.h
++++ b/include/linux/rpmsg.h
+@@ -329,4 +329,8 @@ int rpmsg_trysend_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst,
+       return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false);
+ }
++int get_virtproc_id(struct virtproc_info *vrp);
++struct rpmsg_channel *rpmsg_create_channel(int vrp_id, const char *name,
++                                                      int src, int dst);
++
+ #endif /* _LINUX_RPMSG_H */
+diff --git a/include/linux/socket.h b/include/linux/socket.h
+index 9a546ff..a0dc8f1 100644
+--- a/include/linux/socket.h
++++ b/include/linux/socket.h
+@@ -178,7 +178,8 @@ struct ucred {
+ #define AF_CAIF               37      /* CAIF sockets                 */
+ #define AF_ALG                38      /* Algorithm sockets            */
+ #define AF_NFC                39      /* NFC sockets                  */
+-#define AF_MAX                40      /* For now.. */
++#define AF_RPMSG      40      /* Remote-processor messaging   */
++#define AF_MAX                41      /* For now.. */
+ /* Protocol families, same as address families. */
+ #define PF_UNSPEC     AF_UNSPEC
+@@ -221,6 +222,7 @@ struct ucred {
+ #define PF_CAIF               AF_CAIF
+ #define PF_ALG                AF_ALG
+ #define PF_NFC                AF_NFC
++#define PF_RPMSG      AF_RPMSG
+ #define PF_MAX                AF_MAX
+ /* Maximum queue length specifiable by listen.  */
+@@ -296,6 +298,7 @@ struct ucred {
+ #define SOL_IUCV      277
+ #define SOL_CAIF      278
+ #define SOL_ALG               279
++#define SOL_RPMSG     280
+ /* IPX options */
+ #define IPX_TYPE      1
+diff --git a/include/net/rpmsg.h b/include/net/rpmsg.h
+new file mode 100644
+index 0000000..ab5b174
+--- /dev/null
++++ b/include/net/rpmsg.h
+@@ -0,0 +1,58 @@
++/*
++ * Remote processor messaging sockets
++ *
++ * Copyright (C) 2011 Texas Instruments, Inc
++ *
++ * Ohad Ben-Cohen <ohad@wizery.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#ifndef __NET_RPMSG_H
++#define __NET_RPMSG_H
++
++#include <linux/types.h>
++#include <linux/socket.h>
++
++/* user space needs this */
++#ifndef AF_RPMSG
++#define AF_RPMSG      40
++#define PF_RPMSG      AF_RPMSG
++#endif
++
++/* Connection and socket states */
++enum {
++      RPMSG_CONNECTED = 1, /* wait_for_packet() wants this... */
++      RPMSG_OPEN,
++      RPMSG_LISTENING,
++      RPMSG_CLOSED,
++};
++
++struct sockaddr_rpmsg {
++      sa_family_t family;
++      __u32 vproc_id;
++      __u32 addr;
++};
++
++#define RPMSG_LOCALHOST ((__u32) ~0UL)
++
++#ifdef __KERNEL__
++
++#include <net/sock.h>
++#include <linux/rpmsg.h>
++
++struct rpmsg_socket {
++      struct sock sk;
++      struct rpmsg_channel *rpdev;
++      bool unregister_rpdev;
++};
++
++#endif /* __KERNEL__ */
++#endif /* __NET_RPMSG_H */
+diff --git a/net/Makefile b/net/Makefile
+index 4f4ee08..764e6de 100644
+--- a/net/Makefile
++++ b/net/Makefile
+@@ -70,3 +70,4 @@ obj-$(CONFIG_CEPH_LIB)               += ceph/
+ obj-$(CONFIG_BATMAN_ADV)      += batman-adv/
+ obj-$(CONFIG_NFC)             += nfc/
+ obj-$(CONFIG_OPENVSWITCH)     += openvswitch/
++obj-$(CONFIG_RPMSG)           += rpmsg/
+diff --git a/net/rpmsg/Makefile b/net/rpmsg/Makefile
+new file mode 100644
+index 0000000..4db19df
+--- /dev/null
++++ b/net/rpmsg/Makefile
+@@ -0,0 +1 @@
++obj-$(CONFIG_RPMSG)   += rpmsg_proto.o
+diff --git a/net/rpmsg/rpmsg_proto.c b/net/rpmsg/rpmsg_proto.c
+new file mode 100644
+index 0000000..bb4b78a
+--- /dev/null
++++ b/net/rpmsg/rpmsg_proto.c
+@@ -0,0 +1,623 @@
++/*
++ * AF_RPMSG: Remote processor messaging sockets
++ *
++ * Copyright (C) 2011 Texas Instruments, Inc.
++ *
++ * Ohad Ben-Cohen <ohad@wizery.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * version 2 as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#define pr_fmt(fmt)    "%s: " fmt, __func__
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/types.h>
++#include <linux/list.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
++#include <linux/rwlock.h>
++#include <linux/err.h>
++#include <linux/mutex.h>
++#include <linux/rpmsg.h>
++#include <net/sock.h>
++#include <net/rpmsg.h>
++#include <linux/radix-tree.h>
++
++#define RPMSG_CB(skb) (*(struct sockaddr_rpmsg *)&((skb)->cb))
++
++/*
++ * Used to distinguish between bound and connected socket channels in the
++ * radix tree index space.
++ * Must match value as in drivers/rpmsg/virtio_rpsmg_bus.c:
++ */
++#define RPMSG_RESERVED_ADDRESSES     (1024)
++
++/*
++ * A two-level radix-tree-based scheme is used to maintain the rpmsg channels
++ * we're exposing to userland. The first radix tree maps vproc index id
++ * to its channels, and the second radix tree associates each channel
++ * with its destination addresses (so sockaddr_rpmsg lookups are quick).
++ *
++ * Currently only channels with a valid dst address are supported (aka 'client'
++ * channels as opposed to 'server' channels which usually only have a valid
++ * src address).
++ */
++static RADIX_TREE(rpmsg_channels, GFP_KERNEL);
++
++/*
++ * Synchronization of access to the tree is achieved using a mutex,
++ * because we're using non-atomic radix tree allocations.
++ */
++static DEFINE_MUTEX(rpmsg_channels_lock);
++
++static struct proto rpmsg_proto = {
++      .name           = "RPMSG",
++      .owner          = THIS_MODULE,
++      .obj_size = sizeof(struct rpmsg_socket),
++};
++
++static int rpmsg_sock_connect(struct socket *sock, struct sockaddr *addr,
++                                                      int alen, int flags)
++{
++      struct sock *sk = sock->sk;
++      struct rpmsg_socket *rpsk;
++      struct sockaddr_rpmsg *sa;
++      int err = 0;
++      struct radix_tree_root *vrp_channels;
++      struct rpmsg_channel *rpdev;
++
++      pr_debug("sk %p\n", sk);
++
++      if (sk->sk_state != RPMSG_OPEN)
++              return -EBADFD;
++
++      if (sk->sk_type != SOCK_SEQPACKET)
++              return -EINVAL;
++
++      if (!addr || addr->sa_family != AF_RPMSG)
++              return -EINVAL;
++
++      if (alen < sizeof(*sa))
++              return -EINVAL;
++
++      sa = (struct sockaddr_rpmsg *) addr;
++
++      lock_sock(sk);
++
++      rpsk = container_of(sk, struct rpmsg_socket, sk);
++
++      mutex_lock(&rpmsg_channels_lock);
++
++      /* find the set of channels exposed by this remote processor */
++      vrp_channels = radix_tree_lookup(&rpmsg_channels, sa->vproc_id);
++      if (!vrp_channels) {
++              err = -EINVAL;
++              goto out;
++      }
++
++      /* find the specific channel we need to connect with, by dst addr:  */
++      rpdev = radix_tree_lookup(vrp_channels, sa->addr);
++      if (!rpdev) {
++              err = -EINVAL;
++              goto out;
++      }
++
++      rpsk->rpdev = rpdev;
++
++      /* bind this socket with its rpmsg endpoint */
++      rpdev->ept->priv = sk;
++
++      /* XXX take care of disconnection state too */
++      sk->sk_state = RPMSG_CONNECTED;
++
++out:
++      mutex_unlock(&rpmsg_channels_lock);
++      release_sock(sk);
++      return err;
++}
++
++static int rpmsg_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
++                                      struct msghdr *msg, size_t len)
++{
++      struct sock *sk = sock->sk;
++      struct rpmsg_socket *rpsk;
++      char payload[512];/* todo: sane payload length methodology */
++      int err;
++
++      pr_debug("sk %p len %d\n", sk, len);
++
++      /* XXX check for sock_error as well ? */
++      /* XXX handle noblock ? */
++      if (msg->msg_flags & MSG_OOB)
++              return -EOPNOTSUPP;
++
++      /* no payload ? */
++      if (msg->msg_iov->iov_base == NULL)
++              return -EINVAL;
++
++      lock_sock(sk);
++
++      /* we don't support loopback at this point */
++      if (sk->sk_state != RPMSG_CONNECTED) {
++              release_sock(sk);
++              return -ENOTCONN;
++      }
++
++      rpsk = container_of(sk, struct rpmsg_socket, sk);
++
++      /* XXX for now, ignore the peer address. later use it
++       * with rpmsg_sendto, but only if user is root */
++
++      err = memcpy_fromiovec(payload, msg->msg_iov, len);
++      if (err)
++              goto out;
++
++      /* XXX add length validation */
++      err = rpmsg_send(rpsk->rpdev, payload, len);
++      if (err)
++              pr_err("rpmsg_send failed: %d\n", err);
++
++out:
++      release_sock(sk);
++      return err;
++}
++
++static int rpmsg_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
++                              struct msghdr *msg, size_t len, int flags)
++{
++      struct sock *sk = sock->sk;
++      struct sockaddr_rpmsg *sa;
++      struct sk_buff *skb;
++      int noblock = flags & MSG_DONTWAIT;
++      int ret;
++
++      pr_debug("sk %p len %d\n", sk, len);
++
++      if (flags & MSG_OOB) {
++              pr_err("MSG_OOB: %d\n", EOPNOTSUPP);
++              return -EOPNOTSUPP;
++      }
++
++      msg->msg_namelen = 0;
++
++      skb = skb_recv_datagram(sk, flags, noblock, &ret);
++      if (!skb) {
++              /* check for shutdown ? */
++              pr_err("skb_recv_datagram: %d\n", ret);
++              return ret;
++      }
++
++      if (msg->msg_name) {
++              msg->msg_namelen = sizeof(*sa);
++              sa = (struct sockaddr_rpmsg *) msg->msg_name;
++              sa->vproc_id = RPMSG_CB(skb).vproc_id;
++              sa->addr = RPMSG_CB(skb).addr;
++              sa->family = AF_RPMSG;
++      }
++
++      if (len > skb->len) {
++              len = skb->len;
++      } else if (len < skb->len) {
++              pr_warn("user buffer is too small\n");
++              /* XXX truncate or error ? */
++              msg->msg_flags |= MSG_TRUNC;
++      }
++
++      ret = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len);
++      if (ret) {
++              pr_err("error copying skb data: %d\n", ret);
++              goto out_free;
++      }
++
++      ret = len;
++
++out_free:
++      skb_free_datagram(sk, skb);
++      return ret;
++}
++
++static unsigned int rpmsg_sock_poll(struct file *file, struct socket *sock,
++                                                      poll_table *wait)
++{
++      struct sock *sk = sock->sk;
++      unsigned int mask = 0;
++
++      pr_debug("sk %p\n", sk);
++
++      poll_wait(file, sk_sleep(sk), wait);
++
++      /* exceptional events? */
++      if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
++              mask |= POLLERR;
++      if (sk->sk_shutdown & RCV_SHUTDOWN)
++              mask |= POLLRDHUP;
++      if (sk->sk_shutdown == SHUTDOWN_MASK)
++              mask |= POLLHUP;
++
++      /* readable? */
++      if (!skb_queue_empty(&sk->sk_receive_queue) ||
++          (sk->sk_shutdown & RCV_SHUTDOWN))
++              mask |= POLLIN | POLLRDNORM;
++
++      if (sk->sk_state == RPMSG_CLOSED)
++              mask |= POLLHUP;
++
++      /*
++       * XXX is writable ?
++       * this depends on the destination processor.
++       * if loopback: we're writable unless no memory
++       * if to remote: we need enabled rpmsg buffer or user supplied bufs
++       * for now, let's always be writable.
++       */
++      mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
++
++      return mask;
++}
++EXPORT_SYMBOL(rpmsg_sock_poll);
++
++/*
++ * return bound socket address information, either local or remote
++ * note: len is just an output parameter, doesn't carry any input value
++ */
++static int rpmsg_sock_getname(struct socket *sock, struct sockaddr *addr,
++                                                      int *len, int peer)
++{
++      struct sock *sk = sock->sk;
++      struct rpmsg_socket *rpsk;
++      struct rpmsg_channel *rpdev;
++      struct sockaddr_rpmsg *sa;
++
++      pr_debug("sk %p\n", sk);
++
++      rpsk = container_of(sk, struct rpmsg_socket, sk);
++      rpdev = rpsk->rpdev;
++
++      if (!rpdev)
++              return -ENOTCONN;
++
++      addr->sa_family = AF_RPMSG;
++
++      sa = (struct sockaddr_rpmsg *) addr;
++
++      *len = sizeof(*sa);
++
++      if (peer) {
++              sa->vproc_id = get_virtproc_id(rpdev->vrp);
++              sa->addr = rpdev->dst;
++      } else {
++              sa->vproc_id = RPMSG_LOCALHOST;
++              sa->addr = rpsk->rpdev->src;
++      }
++
++      return 0;
++}
++
++static int rpmsg_sock_release(struct socket *sock)
++{
++      struct sock *sk = sock->sk;
++      struct rpmsg_socket *rpsk = container_of(sk, struct rpmsg_socket, sk);
++
++      pr_debug("sk %p\n", sk);
++
++      if (!sk)
++              return 0;
++
++      if (rpsk->unregister_rpdev)
++              device_unregister(&rpsk->rpdev->dev);
++
++      sock_put(sock->sk);
++
++      return 0;
++}
++
++/*
++ * Notes:
++ * - calling connect after bind isn't currently supported (is it even needed ?).
++ * - userspace arguments to bind aren't intuitive: one needs to provide
++ *   the vproc id of the remote processor he wants the channel to be shared
++ *   with, and the -local- address he wants the channel to be bind with
++ */
++static int
++rpmsg_sock_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
++{
++      struct sock *sk = sock->sk;
++      struct rpmsg_socket *rpsk = container_of(sk, struct rpmsg_socket, sk);
++      struct rpmsg_channel *rpdev;
++      struct sockaddr_rpmsg *sa = (struct sockaddr_rpmsg *)uaddr;
++
++      pr_debug("sk %p\n", sk);
++
++      if (sock->state == SS_CONNECTED)
++              return -EINVAL;
++
++      if (addr_len != sizeof(*sa))
++              return -EINVAL;
++
++      if (sa->family != AF_RPMSG)
++              return -EINVAL;
++
++      if (rpsk->rpdev)
++              return -EBUSY;
++
++      if (sk->sk_state != RPMSG_OPEN)
++              return -EINVAL;
++
++      rpdev = rpmsg_create_channel(sa->vproc_id, "rpmsg-proto", sa->addr,
++                                                      RPMSG_ADDR_ANY);
++      if (!rpdev)
++              return -EINVAL;
++
++      rpsk->rpdev = rpdev;
++      rpsk->unregister_rpdev = true;
++
++      /* bind this socket with its rpmsg endpoint */
++      rpdev->ept->priv = sk;
++
++      sk->sk_state = RPMSG_LISTENING;
++
++      return 0;
++}
++
++static const struct proto_ops rpmsg_sock_ops = {
++      .family         = PF_RPMSG,
++      .owner          = THIS_MODULE,
++
++      .release        = rpmsg_sock_release,
++      .connect        = rpmsg_sock_connect,
++      .getname        = rpmsg_sock_getname,
++      .sendmsg        = rpmsg_sock_sendmsg,
++      .recvmsg        = rpmsg_sock_recvmsg,
++      .poll           = rpmsg_sock_poll,
++      .bind           = rpmsg_sock_bind,
++
++      .listen         = sock_no_listen,
++      .accept         = sock_no_accept,
++      .ioctl          = sock_no_ioctl,
++      .mmap           = sock_no_mmap,
++      .socketpair     = sock_no_socketpair,
++      .shutdown       = sock_no_shutdown,
++      .setsockopt     = sock_no_setsockopt,
++      .getsockopt     = sock_no_getsockopt
++};
++
++static void rpmsg_sock_destruct(struct sock *sk)
++{
++}
++
++static int rpmsg_sock_create(struct net *net, struct socket *sock, int proto,
++                              int kern)
++{
++      struct sock *sk;
++
++      if (sock->type != SOCK_SEQPACKET)
++              return -ESOCKTNOSUPPORT;
++      if (proto != 0)
++              return -EPROTONOSUPPORT;
++
++      sk = sk_alloc(net, PF_RPMSG, GFP_KERNEL, &rpmsg_proto);
++      if (!sk)
++              return -ENOMEM;
++
++      pr_debug("sk %p\n", sk);
++
++      sock->state = SS_UNCONNECTED;
++      sock->ops = &rpmsg_sock_ops;
++      sock_init_data(sock, sk);
++
++      sk->sk_destruct = rpmsg_sock_destruct;
++      sk->sk_protocol = proto;
++
++      sk->sk_state = RPMSG_OPEN;
++
++      return 0;
++}
++
++static const struct net_proto_family rpmsg_proto_family = {
++      .family = PF_RPMSG,
++      .create = rpmsg_sock_create,
++      .owner = THIS_MODULE,
++};
++
++static void __rpmsg_proto_cb(struct device *dev, int from_vproc_id, void *data,
++                                      int len, struct sock *sk, u32 src)
++{
++      struct rpmsg_socket *rpsk = container_of(sk, struct rpmsg_socket, sk);
++      struct sk_buff *skb;
++      int ret;
++
++#ifdef DEBUG_VERBOSE /* Quiet this to improve rpmsg benchmarks: */
++      print_hex_dump(KERN_DEBUG, __func__, DUMP_PREFIX_NONE, 16, 1,
++                     data, len,  true);
++#endif
++
++      lock_sock(sk);
++
++      switch (sk->sk_state) {
++      case RPMSG_CONNECTED:
++              if (rpsk->rpdev->dst != src)
++                      dev_warn(dev, "unexpected source address: %d\n", src);
++              break;
++      case RPMSG_LISTENING:
++              /* When an inbound message is received while we're listening,
++               * we implicitly become connected */
++              sk->sk_state = RPMSG_CONNECTED;
++              rpsk->rpdev->dst = src;
++              break;
++      default:
++              dev_warn(dev, "unexpected inbound message (from %d)\n", src);
++              break;
++      }
++
++      skb = sock_alloc_send_skb(sk, len, 1, &ret);
++      if (!skb) {
++              dev_err(dev, "sock_alloc_send_skb failed: %d\n", ret);
++              ret = -ENOMEM;
++              goto out;
++      }
++
++      RPMSG_CB(skb).vproc_id = from_vproc_id;
++      RPMSG_CB(skb).addr = src;
++      RPMSG_CB(skb).family = AF_RPMSG;
++
++      memcpy(skb_put(skb, len), data, len);
++
++      ret = sock_queue_rcv_skb(sk, skb);
++      if (ret) {
++              dev_err(dev, "sock_queue_rcv_skb failed: %d\n", ret);
++              kfree_skb(skb);
++      }
++
++out:
++      release_sock(sk);
++}
++
++static void rpmsg_proto_cb(struct rpmsg_channel *rpdev, void *data, int len,
++                                              void *priv, u32 src)
++{
++      int id = get_virtproc_id(rpdev->vrp);
++
++      __rpmsg_proto_cb(&rpdev->dev, id, data, len, priv, src);
++}
++
++/* every channel we're probed with is exposed to userland via the Socket API */
++static int rpmsg_proto_probe(struct rpmsg_channel *rpdev)
++{
++      struct device *dev = &rpdev->dev;
++      int ret, dst = rpdev->dst, src = rpdev->src, id;
++      struct radix_tree_root *vrp_channels;
++
++      if (dst == RPMSG_ADDR_ANY)
++              return 0;
++
++      id = get_virtproc_id(rpdev->vrp);
++
++      mutex_lock(&rpmsg_channels_lock);
++
++      /* are we exposing channels for this remote processor yet ? */
++      vrp_channels = radix_tree_lookup(&rpmsg_channels, id);
++      /* not yet ? let's prepare the 2nd radix tree level then */
++      if (!vrp_channels) {
++              vrp_channels = kzalloc(sizeof(*vrp_channels), GFP_KERNEL);
++              INIT_RADIX_TREE(vrp_channels, GFP_KERNEL);
++              /* now let's associate the new channel with its vrp */
++              ret = radix_tree_insert(&rpmsg_channels, id, vrp_channels);
++              if (ret) {
++                      dev_err(dev, "radix_tree_insert failed: %d\n", ret);
++                      kfree(vrp_channels);
++                      return ret;
++              }
++      }
++
++      /* let's associate the new channel with its dst */
++      dev_info(dev, "inserting rpmsg src: %d, dst: %d\n", src, dst);
++      ret = radix_tree_insert(vrp_channels, dst, rpdev);
++      if (ret)
++              dev_err(dev, "failed to add rpmsg addr %d: %d\n", dst, ret);
++
++      mutex_unlock(&rpmsg_channels_lock);
++
++      return ret;
++}
++
++static void rpmsg_proto_remove(struct rpmsg_channel *rpdev)
++{
++      struct device *dev = &rpdev->dev;
++      int id, dst = rpdev->dst, src = rpdev->src;
++      struct radix_tree_root *vrp_channels;
++
++      if (dst == RPMSG_ADDR_ANY)
++              return;
++
++      id = get_virtproc_id(rpdev->vrp);
++
++      mutex_lock(&rpmsg_channels_lock);
++
++      vrp_channels = radix_tree_lookup(&rpmsg_channels, id);
++      if (!vrp_channels) {
++              dev_err(dev, "can't find channels for this vrp: %d\n", id);
++              goto out;
++      }
++
++      /* Only remove non-reserved channels from the tree, as only these
++       * were "probed".  Note: bind is not causing a probe, and bound
++       * sockets have src addresses < RPMSG_RESERVED_ADDRESSES.
++       */
++      if (src >= RPMSG_RESERVED_ADDRESSES) {
++              dev_info(dev, "deleting rpmsg src: %d, dst: %d\n", src, dst);
++              if (!radix_tree_delete(vrp_channels, dst))
++                      dev_err(dev, "failed to delete rpmsg %d\n", dst);
++      }
++
++out:
++      mutex_unlock(&rpmsg_channels_lock);
++}
++
++static struct rpmsg_device_id rpmsg_proto_id_table[] = {
++      { .name = "rpmsg-proto" },
++      { },
++};
++MODULE_DEVICE_TABLE(rpmsg, rpmsg_proto_id_table);
++
++static struct rpmsg_driver rpmsg_proto_driver = {
++      .drv.name       = KBUILD_MODNAME,
++      .drv.owner      = THIS_MODULE,
++      .id_table       = rpmsg_proto_id_table,
++      .probe          = rpmsg_proto_probe,
++      .callback       = rpmsg_proto_cb,
++      .remove         = rpmsg_proto_remove,
++};
++
++static int __init rpmsg_proto_init(void)
++{
++      int ret;
++
++      ret = proto_register(&rpmsg_proto, 0);
++      if (ret) {
++              pr_err("proto_register failed: %d\n", ret);
++              return ret;
++      }
++
++      ret = sock_register(&rpmsg_proto_family);
++      if (ret) {
++              pr_err("sock_register failed: %d\n", ret);
++              goto proto_unreg;
++      }
++
++      /* gimme rpmsg channels to expose ! */
++      ret = register_rpmsg_driver(&rpmsg_proto_driver);
++      if (ret) {
++              pr_err("register_rpmsg_driver failed: %d\n", ret);
++              goto sock_unreg;
++      }
++
++      return 0;
++
++sock_unreg:
++      sock_unregister(PF_RPMSG);
++proto_unreg:
++      proto_unregister(&rpmsg_proto);
++      return ret;
++}
++
++static void __exit rpmsg_proto_exit(void)
++{
++      unregister_rpmsg_driver(&rpmsg_proto_driver);
++      sock_unregister(PF_RPMSG);
++      proto_unregister(&rpmsg_proto);
++}
++
++module_init(rpmsg_proto_init);
++module_exit(rpmsg_proto_exit);
++
++MODULE_DESCRIPTION("Remote processor messaging protocol");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS_NETPROTO(AF_RPMSG);
+-- 
+1.7.9.4
+