aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rpmsg
diff options
context:
space:
mode:
authorErik Rainey <erik.rainey@ti.com>2012-10-01 12:54:31 -0500
committerErik Rainey <erik.rainey@ti.com>2012-10-08 11:41:54 -0500
commitd0cc1fe1f9d322736c5a3495fd2f137c601615ef (patch)
treed8e816e6bad68522354181adf8210eba237e4e08 /drivers/rpmsg
parentcda0054a3304d5fb744e4ff26f5f72b4fcf1a572 (diff)
downloadkernel_samsung_espresso10-d0cc1fe1f9d322736c5a3495fd2f137c601615ef.zip
kernel_samsung_espresso10-d0cc1fe1f9d322736c5a3495fd2f137c601615ef.tar.gz
kernel_samsung_espresso10-d0cc1fe1f9d322736c5a3495fd2f137c601615ef.tar.bz2
OMAPRPC: Updates to OMAPRPC from 3.4 Kernel tree changes.
This patch contains fixes from * lindent * checkPatch * sparse * comments from 3.4 fixes in offline email * moved code from drivers/staging/omaprpc to drivers/rpmsg/omaprpc * Fixes for offset length checking in ION case * made debugging verboseness switchable from kernel boot args if supplied. An RPMSG driver that exposes the Remote Procedure Call API to user space, in order to allow applications to distribute remote calls to more power-efficient remote processors on OMAP systems. Like DOMX it uses the existing RPMSG driver. Used for Distributed Vision Processing. 3.4 branch commit : 34b5939f30e1d5c0c0f2742b4a080b700d01df6f Change-Id: Ib428e7a346ce11db371b578a2ff41239db7db483 Signed-off-by: Erik Rainey <erik.rainey@ti.com>
Diffstat (limited to 'drivers/rpmsg')
-rw-r--r--drivers/rpmsg/Kconfig14
-rw-r--r--drivers/rpmsg/Makefile4
-rw-r--r--drivers/rpmsg/omaprpc/Makefile29
-rw-r--r--drivers/rpmsg/omaprpc/omap_rpc.c1322
-rw-r--r--drivers/rpmsg/omaprpc/omap_rpc_dmabuf.c449
-rw-r--r--drivers/rpmsg/omaprpc/omap_rpc_internal.h215
-rw-r--r--drivers/rpmsg/omaprpc/omap_rpc_ion.c382
-rw-r--r--drivers/rpmsg/omaprpc/omap_rpc_rproc.c93
-rw-r--r--drivers/rpmsg/omaprpc/omap_rpc_tiler.c59
9 files changed, 2566 insertions, 1 deletions
diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
index 4625ed6..aa8413f 100644
--- a/drivers/rpmsg/Kconfig
+++ b/drivers/rpmsg/Kconfig
@@ -28,6 +28,20 @@ config RPMSG_OMX
If unsure, say N.
+config RPC_OMAP
+ tristate "OMAP Remote Procedure Call driver"
+ default y
+ depends on RPMSG
+ depends on REMOTEPROC || REMOTE_PROC
+ depends on OMAP_REMOTEPROC || OMAP_REMOTE_PROC
+ depends on (TI_TILER && ION_OMAP) || (DMA_SHARED_BUFFER && DRM_OMAP)
+ ---help---
+ An rpmsg driver that exposes the Remote Procedure Call API to
+ user space, in order to allow applications to distribute
+ remote calls to more power-efficient remote processors on OMAP4+ systems.
+
+ If unsure, say N.
+
config RPMSG_RESMGR
tristate "rpmsg resource manager"
default y
diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile
index 1b0e04b..56d1a1f 100644
--- a/drivers/rpmsg/Makefile
+++ b/drivers/rpmsg/Makefile
@@ -1,6 +1,8 @@
obj-$(CONFIG_RPMSG) += virtio_rpmsg_bus.o
+obj-$(CONFIG_RPMSG_RESMGR) += rpmsg_resmgr.o
obj-$(CONFIG_RPMSG_OMX) += rpmsg_omx.o
+obj-$(CONFIG_RPC_OMAP) += omaprpc/
+
obj-$(CONFIG_RPMSG_CLIENT_SAMPLE) += rpmsg_client_sample.o
obj-$(CONFIG_RPMSG_SERVER_SAMPLE) += rpmsg_server_sample.o
-obj-$(CONFIG_RPMSG_RESMGR) += rpmsg_resmgr.o
diff --git a/drivers/rpmsg/omaprpc/Makefile b/drivers/rpmsg/omaprpc/Makefile
new file mode 100644
index 0000000..c64e4f2
--- /dev/null
+++ b/drivers/rpmsg/omaprpc/Makefile
@@ -0,0 +1,29 @@
+#
+# OMAP Remote Procedure Call Driver.
+#
+# Copyright(c) 2012 Texas Instruments. All rights reserved.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <http://www.gnu.org/licenses/>.
+
+obj-$(CONFIG_RPC_OMAP) += omaprpc.o
+omaprpc-y := omap_rpc.o \
+ omap_rpc_tiler.o \
+ omap_rpc_rproc.o
+
+ifeq ($(CONFIG_ION_OMAP),y)
+omaprpc-y += omap_rpc_ion.o
+else
+ifeq ($(CONFIG_DMA_SHARED_BUFFER),y)
+omaprpc-y += omap_rpc_dmabuf.o
+endif
+endif
diff --git a/drivers/rpmsg/omaprpc/omap_rpc.c b/drivers/rpmsg/omaprpc/omap_rpc.c
new file mode 100644
index 0000000..5f22838
--- /dev/null
+++ b/drivers/rpmsg/omaprpc/omap_rpc.c
@@ -0,0 +1,1322 @@
+/*
+ * OMAP Remote Procedure Call Driver.
+ *
+ * Copyright(c) 2012 Texas Instruments. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "omap_rpc_internal.h"
+
+static struct class *omaprpc_class;
+static dev_t omaprpc_dev;
+
+/* store all remote rpc connection services (usually one per remoteproc) */
+
+/* could also use DEFINE_IDR(omaprpc_services); */
+static struct idr omaprpc_services = IDR_INIT(omaprpc_services);
+
+/* could also use DEFINE_SPINLOCK(omaprpc_services_lock); */
+static spinlock_t omaprpc_services_lock =
+__SPIN_LOCK_UNLOCKED(omaprpc_services_lock);
+
+/* could also use LIST_HEAD(omaprpc_services_list); */
+static struct list_head omaprpc_services_list =
+LIST_HEAD_INIT(omaprpc_services_list);
+
+static struct timeval start_time;
+static struct timeval end_time;
+static long usec_elapsed;
+unsigned int omaprpc_debug;
+EXPORT_SYMBOL(omaprpc_debug);
+MODULE_PARM_DESC(debug, "Used to enable debug messages");
+module_param_named(debug, omaprpc_debug, int, 0600);
+
+static void omaprpc_print_msg(struct omaprpc_instance_t *rpc,
+ char *prefix, char buffer[512])
+{
+ u32 sp = 0, p = 0;
+ struct omaprpc_msg_header_t *hdr =
+ (struct omaprpc_msg_header_t *)buffer;
+ struct omaprpc_instance_handle_t *hdl = NULL;
+ struct omaprpc_instance_info_t *info = NULL;
+ struct omaprpc_packet_t *packet = NULL;
+ struct omaprpc_parameter_t *param = NULL;
+ OMAPRPC_PRINT(OMAPRPC_ZONE_VERBOSE, rpc->rpcserv->dev,
+ "%s HDR: type %d flags: %d len: %d\n",
+ prefix, hdr->msg_type, hdr->msg_flags, hdr->msg_len);
+ switch (hdr->msg_type) {
+ case OMAPRPC_MSG_INSTANCE_CREATED:
+ case OMAPRPC_MSG_INSTANCE_DESTROYED:
+ hdl = OMAPRPC_PAYLOAD(buffer, omaprpc_instance_handle_t);
+ OMAPRPC_PRINT(OMAPRPC_ZONE_VERBOSE,
+ rpc->rpcserv->dev,
+ "%s endpoint:%d status:%d\n",
+ prefix, hdl->endpoint_address, hdl->status);
+ break;
+ case OMAPRPC_MSG_INSTANCE_INFO:
+ info = OMAPRPC_PAYLOAD(buffer, omaprpc_instance_info_t);
+ OMAPRPC_PRINT(OMAPRPC_ZONE_VERBOSE,
+ rpc->rpcserv->dev,
+ "%s (info not yet implemented)\n", prefix);
+ break;
+ case OMAPRPC_MSG_CALL_FUNCTION:
+ packet = OMAPRPC_PAYLOAD(buffer, omaprpc_packet_t);
+ OMAPRPC_PRINT(OMAPRPC_ZONE_VERBOSE,
+ rpc->rpcserv->dev,
+ "%s PACKET: desc:%04x msg_id:%04x pool_id:%04x"
+ " job_id:%04x func:0x%08x result:%d size:%u\n",
+ prefix,
+ packet->desc,
+ packet->msg_id,
+ packet->pool_id,
+ packet->job_id,
+ packet->fxn_idx,
+ packet->result, packet->data_size);
+ sp = sizeof(struct omaprpc_parameter_t);
+ param = (struct omaprpc_parameter_t *)packet->data;
+ for (p = 0; p < (packet->data_size / sp); p++) {
+ OMAPRPC_PRINT(OMAPRPC_ZONE_VERBOSE,
+ rpc->rpcserv->dev,
+ "%s PARAM[%u] size:%zu data:%zu (0x%08x)",
+ prefix, p,
+ param[p].size,
+ param[p].data, param[p].data);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void omaprpc_fxn_del(struct omaprpc_instance_t *rpc)
+{
+ /* Free any outstanding function calls */
+ if (!list_empty(&rpc->fxn_list)) {
+ struct omaprpc_call_function_list_t *pos, *n;
+
+ mutex_lock(&rpc->lock);
+ list_for_each_entry_safe(pos, n, &rpc->fxn_list, list) {
+ list_del(&pos->list);
+ kfree(pos->function);
+ kfree(pos);
+ }
+ mutex_lock(&rpc->lock);
+ }
+}
+
+static struct omaprpc_call_function_t *omaprpc_fxn_get(struct omaprpc_instance_t
+ *rpc, u16 msgId)
+{
+ struct omaprpc_call_function_t *function = NULL;
+ struct omaprpc_call_function_list_t *pos, *n;
+
+ mutex_lock(&rpc->lock);
+ list_for_each_entry_safe(pos, n, &rpc->fxn_list, list) {
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "Looking for msg %u, found msg %u\n",
+ msgId, pos->msgId);
+ if (pos->msgId == msgId) {
+ function = pos->function;
+ list_del(&pos->list);
+ kfree(pos);
+ break;
+ }
+ }
+ mutex_unlock(&rpc->lock);
+ return function;
+}
+
+static int omaprpc_fxn_add(struct omaprpc_instance_t *rpc,
+ struct omaprpc_call_function_t *function, u16 msgId)
+{
+ struct omaprpc_call_function_list_t *fxn = NULL;
+ fxn = (struct omaprpc_call_function_list_t *)
+ kmalloc(sizeof(struct omaprpc_call_function_list_t), GFP_KERNEL);
+ if (fxn) {
+ fxn->function = function;
+ fxn->msgId = msgId;
+ mutex_lock(&rpc->lock);
+ list_add(&fxn->list, &rpc->fxn_list);
+ mutex_unlock(&rpc->lock);
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "Added msg id %u to list", msgId);
+ } else {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Failed to add function %p to list with id %d\n",
+ function, msgId);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/* This is the callback from the remote core to this side */
+static void omaprpc_cb(struct rpmsg_channel *rpdev,
+ void *data, int len, void *priv, u32 src)
+{
+ struct omaprpc_msg_header_t *hdr = data;
+ struct omaprpc_instance_t *rpc = priv;
+ struct omaprpc_instance_handle_t *hdl;
+ struct sk_buff *skb;
+ char *buf = (char *)data;
+ char *skbdata;
+ u32 expected = 0;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "OMAPRPC: <== incoming msg src %d len %d msg_type %d msg_len %d\n",
+ src, len, hdr->msg_type, hdr->msg_len);
+
+ if (omaprpc_debug & OMAPRPC_ZONE_VERBOSE) {
+ print_hex_dump(KERN_DEBUG, "OMAPRPC: RX: ",
+ DUMP_PREFIX_NONE, 16, 1, data, len, true);
+ omaprpc_print_msg(rpc, "RX:", buf);
+ }
+
+ expected = sizeof(struct omaprpc_msg_header_t);
+ switch (hdr->msg_type) {
+ case OMAPRPC_MSG_INSTANCE_CREATED:
+ case OMAPRPC_MSG_INSTANCE_DESTROYED:
+ expected += sizeof(struct omaprpc_instance_handle_t);
+ break;
+ case OMAPRPC_MSG_INSTANCE_INFO:
+ expected += sizeof(struct omaprpc_instance_info_t);
+ break;
+ }
+
+ if (len < expected) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "OMAPRPC: truncated message detected! Was %u bytes long"
+ " expected %u\n", len, expected);
+ rpc->state = OMAPRPC_STATE_FAULT;
+ return;
+ }
+
+ switch (hdr->msg_type) {
+ case OMAPRPC_MSG_INSTANCE_CREATED:
+ hdl = OMAPRPC_PAYLOAD(buf, omaprpc_instance_handle_t);
+ if (hdl->status) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "OMAPRPC: Failed to connect to remote core! "
+ "Status=%d\n", hdl->status);
+ rpc->state = OMAPRPC_STATE_FAULT;
+ } else {
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "OMAPRPC: Created addr %d status %d\n",
+ hdl->endpoint_address, hdl->status);
+ /* only save the address if it connected. */
+ rpc->dst = hdl->endpoint_address;
+ rpc->state = OMAPRPC_STATE_CONNECTED;
+ /* default core */
+ rpc->core = OMAPRPC_CORE_MCU1;
+ }
+ rpc->transisioning = 0;
+ /* unblock the connect function */
+ complete(&rpc->reply_arrived);
+ break;
+ case OMAPRPC_MSG_INSTANCE_DESTROYED:
+ hdl = OMAPRPC_PAYLOAD(buf, omaprpc_instance_handle_t);
+ if (hdr->msg_len < sizeof(*hdl)) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "OMAPRPC: disconnect message was incorrect size!\n");
+ rpc->state = OMAPRPC_STATE_FAULT;
+ break;
+ }
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "OMAPRPC: endpoint %d disconnected!\n",
+ hdl->endpoint_address);
+ rpc->state = OMAPRPC_STATE_DISCONNECTED;
+ rpc->dst = 0;
+ rpc->transisioning = 0;
+ /* unblock the disconnect ioctl call */
+ complete(&rpc->reply_arrived);
+ break;
+ case OMAPRPC_MSG_INSTANCE_INFO:
+ break;
+ case OMAPRPC_MSG_CALL_FUNCTION:
+ case OMAPRPC_MSG_FUNCTION_RETURN:
+
+ if (omaprpc_debug & OMAPRPC_ZONE_PERF) {
+ do_gettimeofday(&end_time);
+ usec_elapsed = (end_time.tv_sec - start_time.tv_sec) *
+ 1000000 + end_time.tv_usec - start_time.tv_usec;
+ OMAPRPC_PRINT(OMAPRPC_ZONE_PERF, rpc->rpcserv->dev,
+ "write to callback took %lu usec\n",
+ usec_elapsed);
+ }
+
+ skb = alloc_skb(hdr->msg_len, GFP_KERNEL);
+ if (!skb) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "OMAPRPC: alloc_skb err: %u\n",
+ hdr->msg_len);
+ break;
+ }
+ skbdata = skb_put(skb, hdr->msg_len);
+ memcpy(skbdata, hdr->msg_data, hdr->msg_len);
+
+ mutex_lock(&rpc->lock);
+
+ if (omaprpc_debug & OMAPRPC_ZONE_PERF) {
+ /* capture the time delay between callback and read */
+ do_gettimeofday(&start_time);
+ }
+
+ skb_queue_tail(&rpc->queue, skb);
+ mutex_unlock(&rpc->lock);
+ /* wake up any blocking processes, waiting for new data */
+ wake_up_interruptible(&rpc->readq);
+ break;
+ default:
+ dev_warn(rpc->rpcserv->dev,
+ "OMAPRPC: unexpected msg type: %d\n", hdr->msg_type);
+ break;
+ }
+}
+
+static int omaprpc_connect(struct omaprpc_instance_t *rpc,
+ struct omaprpc_create_instance_t *connect)
+{
+ struct omaprpc_service_t *rpcserv = rpc->rpcserv;
+ char kbuf[512];
+ struct omaprpc_msg_header_t *hdr =
+ (struct omaprpc_msg_header_t *)&kbuf[0];
+ int ret = 0;
+ u32 len = 0;
+
+ /* Return "is connected" if connected */
+ if (rpc->state == OMAPRPC_STATE_CONNECTED) {
+ dev_dbg(rpcserv->dev, "OMAPRPC: endpoint already connected\n");
+ return -EISCONN;
+ }
+
+ /* Initialize the Connection Request Message */
+ hdr->msg_type = OMAPRPC_MSG_CREATE_INSTANCE;
+ hdr->msg_len = sizeof(struct omaprpc_create_instance_t);
+ memcpy(hdr->msg_data, connect, hdr->msg_len);
+ len = sizeof(struct omaprpc_msg_header_t) + hdr->msg_len;
+
+ /* Initialize a completion object */
+ init_completion(&rpc->reply_arrived);
+
+ /* indicate that we are waiting for a completion */
+ rpc->transisioning = 1;
+
+ /*
+ * send a conn req to the remote RPC connection service. use
+ * the new local address that was just allocated by ->open
+ */
+ ret = rpmsg_send_offchannel(rpcserv->rpdev,
+ rpc->ept->addr,
+ rpcserv->rpdev->dst, (char *)kbuf, len);
+ if (ret > 0) {
+ OMAPRPC_ERR(rpcserv->dev,
+ "OMAPRPC: rpmsg_send failed: %d\n", ret);
+ return ret;
+ }
+
+ /* wait until a connection reply arrives or 5 seconds elapse */
+ ret = wait_for_completion_interruptible_timeout(&rpc->reply_arrived,
+ msecs_to_jiffies(5000));
+ if (rpc->state == OMAPRPC_STATE_CONNECTED)
+ return 0;
+
+ if (rpc->state == OMAPRPC_STATE_FAULT)
+ return -ENXIO;
+
+ if (ret > 0) {
+ OMAPRPC_ERR(rpcserv->dev,
+ "OMAPRPC: premature wakeup: %d\n", ret);
+ return -EIO;
+ }
+
+ return -ETIMEDOUT;
+}
+
+static long omaprpc_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct omaprpc_instance_t *rpc = filp->private_data;
+ struct omaprpc_service_t *rpcserv = rpc->rpcserv;
+ struct omaprpc_create_instance_t connect;
+ int ret = 0;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpcserv->dev,
+ "OMAPRPC: %s: cmd %d, arg 0x%lx\n", __func__, cmd, arg);
+
+ /*
+ * if the magic was not present, tell the caller
+ *that we are not a typewritter[sic]!
+ */
+ if (_IOC_TYPE(cmd) != OMAPRPC_IOC_MAGIC)
+ return -ENOTTY;
+
+ /*
+ * if the number of the command is larger than what
+ * we support, also tell the caller that we are not a typewriter[sic]!
+ */
+ if (_IOC_NR(cmd) > OMAPRPC_IOC_MAXNR)
+ return -ENOTTY;
+
+ switch (cmd) {
+ case OMAPRPC_IOC_CREATE:
+ /* copy the connection buffer from the user */
+ ret = copy_from_user(&connect, (char __user *)arg,
+ sizeof(connect));
+ if (ret) {
+ OMAPRPC_ERR(rpcserv->dev,
+ "OMAPRPC: %s: %d: copy_from_user fail: %d\n",
+ __func__, _IOC_NR(cmd), ret);
+ ret = -EFAULT;
+ } else {
+ /* make sure user input is null terminated */
+ connect.name[sizeof(connect.name) - 1] = '\0';
+ /* connect to the remote core */
+ ret = omaprpc_connect(rpc, &connect);
+ }
+ break;
+ case OMAPRPC_IOC_DESTROY:
+ break;
+#if defined(OMAPRPC_USE_ION)
+ case OMAPRPC_IOC_IONREGISTER:
+ {
+ struct ion_fd_data data;
+ if (copy_from_user
+ (&data, (char __user *)arg, sizeof(data))) {
+ ret = -EFAULT;
+ OMAPRPC_ERR(rpcserv->dev,
+ "OMAPRPC: %s: %d: copy_from_user fail: %d\n",
+ __func__, _IOC_NR(cmd), ret);
+ }
+ data.handle = ion_import_fd(rpc->ion_client, data.fd);
+ if (IS_ERR(data.handle))
+ data.handle = NULL;
+ if (copy_to_user
+ ((char __user *)arg, &data, sizeof(data))) {
+ ret = -EFAULT;
+ OMAPRPC_ERR(rpcserv->dev,
+ "OMAPRPC: %s: %d: copy_to_user fail: %d\n",
+ __func__, _IOC_NR(cmd), ret);
+ }
+ break;
+ }
+ case OMAPRPC_IOC_IONUNREGISTER:
+ {
+ struct ion_fd_data data;
+ if (copy_from_user
+ (&data, (char __user *)arg, sizeof(data))) {
+ ret = -EFAULT;
+ OMAPRPC_ERR(rpcserv->dev,
+ "OMAPRPC: %s: %d: copy_from_user fail: %d\n",
+ __func__, _IOC_NR(cmd), ret);
+ }
+ ion_free(rpc->ion_client, data.handle);
+ if (copy_to_user
+ ((char __user *)arg, &data, sizeof(data))) {
+ ret = -EFAULT;
+ OMAPRPC_ERR(rpcserv->dev,
+ "OMAPRPC: %s: %d: copy_to_user fail: %d\n",
+ __func__, _IOC_NR(cmd), ret);
+ }
+ break;
+ }
+#endif
+ default:
+ OMAPRPC_ERR(rpcserv->dev,
+ "OMAPRPC: unhandled ioctl cmd: %d\n", cmd);
+ break;
+ }
+
+ return ret;
+}
+
+static int omaprpc_open(struct inode *inode, struct file *filp)
+{
+ struct omaprpc_service_t *rpcserv;
+ struct omaprpc_instance_t *rpc;
+
+ /* get the service pointer out of the inode */
+ rpcserv = container_of(inode->i_cdev, struct omaprpc_service_t, cdev);
+
+ /*
+ * Return EBUSY if we are down and if non-blocking or waiting for
+ * something
+ */
+ if (rpcserv->state == OMAPRPC_SERVICE_STATE_DOWN)
+ if ((filp->f_flags & O_NONBLOCK) ||
+ wait_for_completion_interruptible(&rpcserv->comp))
+ return -EBUSY;
+
+ /* Create a new instance */
+ rpc = kzalloc(sizeof(*rpc), GFP_KERNEL);
+ if (!rpc)
+ return -ENOMEM;
+
+ /* Initialize the instance mutex */
+ mutex_init(&rpc->lock);
+
+ /* Initialize the queue head for the socket buffers */
+ skb_queue_head_init(&rpc->queue);
+
+ /* Initialize the reading queue */
+ init_waitqueue_head(&rpc->readq);
+
+ /* Save the service pointer within the instance for later reference */
+ rpc->rpcserv = rpcserv;
+ rpc->state = OMAPRPC_STATE_DISCONNECTED;
+ rpc->transisioning = 0;
+
+ /* Initialize the current msg id */
+ rpc->msgId = 0;
+
+ /* Initialize the remember function call list */
+ INIT_LIST_HEAD(&rpc->fxn_list);
+
+#if defined(OMAPRPC_USE_DMABUF)
+ INIT_LIST_HEAD(&rpc->dma_list);
+#endif
+
+ /*
+ * assign a new, unique, local address and
+ * associate the instance with it
+ */
+ rpc->ept = rpmsg_create_ept(rpcserv->rpdev, omaprpc_cb, rpc,
+ RPMSG_ADDR_ANY);
+ if (!rpc->ept) {
+ OMAPRPC_ERR(rpcserv->dev, "OMAPRPC: create ept failed\n");
+ kfree(rpc);
+ return -ENOMEM;
+ }
+#if defined(OMAPRPC_USE_ION)
+ /* get a handle to the ion client for RPC buffers */
+ rpc->ion_client = ion_client_create(omap_ion_device,
+ (1 << ION_HEAP_TYPE_CARVEOUT) |
+ (1 << OMAP_ION_HEAP_TYPE_TILER),
+ "rpmsg-rpc");
+#endif
+
+ /* remember rpc in filp's private data */
+ filp->private_data = rpc;
+
+ /* Add the instance to the service's list */
+ mutex_lock(&rpcserv->lock);
+ list_add(&rpc->list, &rpcserv->instance_list);
+ mutex_unlock(&rpcserv->lock);
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpcserv->dev,
+ "OMAPRPC: local addr assigned: 0x%x\n", rpc->ept->addr);
+
+ return 0;
+}
+
+static int omaprpc_release(struct inode *inode, struct file *filp)
+{
+ char kbuf[512];
+ u32 len = 0;
+ int ret = 0;
+ struct omaprpc_instance_t *rpc = NULL;
+ struct omaprpc_service_t *rpcserv = NULL;
+ if (inode == NULL || filp == NULL)
+ return 0;
+
+ /* a conveinence pointer to the instane */
+ rpc = filp->private_data;
+ /* a conveinence pointer to service */
+ rpcserv = rpc->rpcserv;
+ if (rpc == NULL || rpcserv == NULL)
+ return 0;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpcserv->dev,
+ "Releasing Instance %p, in state %d\n", rpc, rpc->state);
+ /* if we are in a normal state */
+ if (rpc->state != OMAPRPC_STATE_FAULT) {
+ /* if we have connected already */
+ if (rpc->state == OMAPRPC_STATE_CONNECTED) {
+ struct omaprpc_msg_header_t *hdr =
+ (struct omaprpc_msg_header_t *)&kbuf[0];
+ struct omaprpc_instance_handle_t *handle =
+ OMAPRPC_PAYLOAD(kbuf, omaprpc_instance_handle_t);
+
+ /* Format a disconnect message */
+ hdr->msg_type = OMAPRPC_MSG_DESTROY_INSTANCE;
+ hdr->msg_len = sizeof(u32);
+ /* end point address */
+ handle->endpoint_address = rpc->dst;
+ handle->status = 0;
+ len =
+ sizeof(struct omaprpc_msg_header_t) + hdr->msg_len;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpcserv->dev,
+ "OMAPRPC: Disconnecting from RPC service at %d\n",
+ rpc->dst);
+
+ /* send the msg to the remote RPC connection service */
+ ret = rpmsg_send_offchannel(rpcserv->rpdev,
+ rpc->ept->addr,
+ rpcserv->rpdev->dst,
+ (char *)kbuf, len);
+ if (ret) {
+ OMAPRPC_ERR(rpcserv->dev,
+ "OMAPRPC: rpmsg_send failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ /*
+ * TODO: Should we wait for a message to come back?
+ * For now, no.
+ */
+ wait_for_completion(&rpc->reply_arrived);
+
+ }
+
+ /* Destroy the local endpoint */
+ if (rpc->ept) {
+ rpmsg_destroy_ept(rpc->ept);
+ rpc->ept = NULL;
+ }
+ }
+#if defined(OMAPRPC_USE_ION)
+ if (rpc->ion_client) {
+ /* Destroy our local client to ion */
+ ion_client_destroy(rpc->ion_client);
+ rpc->ion_client = NULL;
+ }
+#endif
+ /* Remove the function list */
+ omaprpc_fxn_del(rpc);
+
+ /* Remove the instance from the service's list */
+ mutex_lock(&rpcserv->lock);
+ list_del(&rpc->list);
+ mutex_unlock(&rpcserv->lock);
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpcserv->dev,
+ "OMAPRPC: Instance %p has been deleted!\n", rpc);
+
+ if (list_empty(&rpcserv->instance_list)) {
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpcserv->dev,
+ "OMAPRPC: All instances have been removed!\n");
+ }
+
+ /* Delete the instance memory */
+ filp->private_data = NULL;
+ memset(rpc, 0xFE, sizeof(struct omaprpc_instance_t));
+ kfree(rpc);
+ rpc = NULL;
+ return 0;
+}
+
+static ssize_t omaprpc_read(struct file *filp,
+ char __user *buf, size_t len, loff_t *offp)
+{
+ struct omaprpc_instance_t *rpc = filp->private_data;
+ struct omaprpc_packet_t *packet = NULL;
+ struct omaprpc_parameter_t *parameters = NULL;
+ struct omaprpc_call_function_t *function = NULL;
+ struct omaprpc_function_return_t returned;
+ struct sk_buff *skb = NULL;
+ int ret = 0;
+ int use = sizeof(returned);
+
+ /* too big */
+ if (len > use) {
+ ret = -EOVERFLOW;
+ goto failure;
+ }
+
+ /* too small */
+ if (len < use) {
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ /* locked */
+ if (mutex_lock_interruptible(&rpc->lock)) {
+ ret = -ERESTARTSYS;
+ goto failure;
+ }
+
+ /* error state */
+ if (rpc->state == OMAPRPC_STATE_FAULT) {
+ mutex_unlock(&rpc->lock);
+ ret = -ENXIO;
+ goto failure;
+ }
+
+ /* not connected to the remote side... */
+ if (rpc->state != OMAPRPC_STATE_CONNECTED) {
+ mutex_unlock(&rpc->lock);
+ ret = -ENOTCONN;
+ goto failure;
+ }
+
+ /* nothing to read ? */
+ if (skb_queue_empty(&rpc->queue)) {
+ mutex_unlock(&rpc->lock);
+ /* non-blocking requested ? return now */
+ if (filp->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ goto failure;
+ }
+ /* otherwise block, and wait for data */
+ if (wait_event_interruptible(rpc->readq,
+ (!skb_queue_empty(&rpc->queue) ||
+ rpc->state ==
+ OMAPRPC_STATE_FAULT))) {
+ ret = -ERESTARTSYS;
+ goto failure;
+ }
+ /* re-grab the lock */
+ if (mutex_lock_interruptible(&rpc->lock)) {
+ ret = -ERESTARTSYS;
+ goto failure;
+ }
+ }
+
+ /* a fault happened while we waited. */
+ if (rpc->state == OMAPRPC_STATE_FAULT) {
+ mutex_unlock(&rpc->lock);
+ ret = -ENXIO;
+ goto failure;
+ }
+
+ /* pull the buffer out of the queue */
+ skb = skb_dequeue(&rpc->queue);
+ if (!skb) {
+ mutex_unlock(&rpc->lock);
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "OMAPRPC: skb was NULL when dequeued, "
+ "possible race condition!\n");
+ ret = -EIO;
+ goto failure;
+ }
+
+ if (omaprpc_debug & OMAPRPC_ZONE_PERF) {
+ do_gettimeofday(&end_time);
+ usec_elapsed = (end_time.tv_sec - start_time.tv_sec) *
+ 1000000 + end_time.tv_usec - start_time.tv_usec;
+ OMAPRPC_PRINT(OMAPRPC_ZONE_PERF, rpc->rpcserv->dev,
+ "callback to read took %lu usec\n", usec_elapsed);
+ }
+
+ /* unlock the instances */
+ mutex_unlock(&rpc->lock);
+
+ packet = (struct omaprpc_packet_t *)skb->data;
+ parameters = (struct omaprpc_parameter_t *)packet->data;
+
+ /* pull the function memory from the list */
+ function = omaprpc_fxn_get(rpc, packet->msg_id);
+ if (function) {
+ if (function->num_translations > 0) {
+ /*
+ * Untranslate the PA pointers back
+ * to the ARM ION handles
+ */
+ ret = omaprpc_xlate_buffers(rpc,
+ function,
+ OMAPRPC_RPA_TO_UVA);
+ if (ret < 0)
+ goto failure;
+ }
+ }
+ returned.func_index = OMAPRPC_FXN_MASK(packet->fxn_idx);
+ returned.status = packet->result;
+
+ /* copy the kernel buffer to the user side */
+ if (copy_to_user(buf, &returned, use)) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "OMAPRPC: %s: copy_to_user fail\n", __func__);
+ ret = -EFAULT;
+ } else {
+ ret = use;
+ }
+failure:
+ kfree(function);
+ kfree_skb(skb);
+ return ret;
+}
+
+static ssize_t omaprpc_write(struct file *filp,
+ const char __user *ubuf,
+ size_t len, loff_t *offp)
+{
+ struct omaprpc_instance_t *rpc = filp->private_data;
+ struct omaprpc_service_t *rpcserv = rpc->rpcserv;
+ struct omaprpc_msg_header_t *hdr = NULL;
+ struct omaprpc_call_function_t *function = NULL;
+ struct omaprpc_packet_t *packet = NULL;
+ struct omaprpc_parameter_t *parameters = NULL;
+ char kbuf[512];
+ int use = 0, ret = 0, param = 0;
+
+ /* incorrect parameter */
+ if (len < sizeof(struct omaprpc_call_function_t)) {
+ ret = -ENOTSUPP;
+ goto failure;
+ }
+
+ /* over OMAPRPC_MAX_TRANSLATIONS! too many! */
+ if (len > (sizeof(struct omaprpc_call_function_t) +
+ OMAPRPC_MAX_TRANSLATIONS *
+ sizeof(struct omaprpc_param_translation_t))) {
+ ret = -ENOTSUPP;
+ goto failure;
+ }
+
+ /* check the state of the driver */
+ if (rpc->state != OMAPRPC_STATE_CONNECTED) {
+ ret = -ENOTCONN;
+ goto failure;
+ }
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpcserv->dev,
+ "OMAPRPC: Allocating local function call copy for %u bytes\n",
+ len);
+
+ function = kzalloc(len, GFP_KERNEL);
+ if (function == NULL) {
+ /* could not allocate enough memory to cover the transaction */
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ /* copy the user packet to out memory */
+ if (copy_from_user(function, ubuf, len)) {
+ ret = -EMSGSIZE;
+ goto failure;
+ }
+
+ /* increment the message ID and wrap if needed */
+ rpc->msgId = (rpc->msgId + 1) & 0xFFFF;
+
+ memset(kbuf, 0, sizeof(kbuf));
+ hdr = (struct omaprpc_msg_header_t *)kbuf;
+ hdr->msg_type = OMAPRPC_MSG_CALL_FUNCTION;
+ hdr->msg_flags = 0;
+ hdr->msg_len = sizeof(struct omaprpc_packet_t);
+ packet = OMAPRPC_PAYLOAD(kbuf, omaprpc_packet_t);
+ packet->desc = OMAPRPC_DESC_EXEC_SYNC;
+ packet->msg_id = rpc->msgId;
+ packet->pool_id = OMAPRPC_POOLID_DEFAULT;
+ packet->job_id = OMAPRPC_JOBID_DISCRETE;
+ packet->fxn_idx = OMAPRPC_SET_FXN_IDX(function->func_index);
+ packet->result = 0;
+ packet->data_size = sizeof(struct omaprpc_parameter_t) *
+ function->num_params;
+
+ /* compute the parameter pointer changes last since this will cause the
+ cache operations */
+ parameters = (struct omaprpc_parameter_t *)packet->data;
+ for (param = 0; param < function->num_params; param++) {
+ parameters[param].size = function->params[param].size;
+ if (function->params[param].type == OMAPRPC_PARAM_TYPE_PTR) {
+ /* internally the buffer translations takes care of the
+ offsets */
+ void *reserved =
+ (void *)function->params[param].reserved;
+ parameters[param].data =
+ (size_t) omaprpc_buffer_lookup(rpc,
+ rpc->core,
+ (virt_addr_t)
+ function->
+ params[param].data,
+ (virt_addr_t)
+ function->
+ params[param].base,
+ reserved);
+ } else if (function->params[param].type ==
+ OMAPRPC_PARAM_TYPE_ATOMIC) {
+ parameters[param].data = function->params[param].data;
+ } else {
+ ret = -ENOTSUPP;
+ goto failure;
+ }
+ }
+
+ /* Compute the size of the RPMSG packet */
+ use = sizeof(*hdr) + hdr->msg_len + packet->data_size;
+
+ /* failed to provide the translation data */
+ if (function->num_translations > 0 &&
+ len < (sizeof(struct omaprpc_call_function_t) +
+ (function->num_translations *
+ sizeof(struct omaprpc_param_translation_t)))) {
+ ret = -ENXIO;
+ goto failure;
+ }
+
+ /* If there are pointers to translate for the user, do so now */
+ if (function->num_translations > 0) {
+ /* alter our copy of function and the user's parameters so
+ that we can send to remote cores */
+ ret = omaprpc_xlate_buffers(rpc, function, OMAPRPC_UVA_TO_RPA);
+ if (ret < 0) {
+ OMAPRPC_ERR(rpcserv->dev,
+ "OMAPRPC: ERROR: Failed to translate all "
+ "pointers for remote core!\n");
+ goto failure;
+ }
+ }
+
+ /* save the function data */
+ ret = omaprpc_fxn_add(rpc, function, rpc->msgId);
+ if (ret < 0) {
+ /* unwind */
+ omaprpc_xlate_buffers(rpc, function, OMAPRPC_RPA_TO_UVA);
+ goto failure;
+ }
+
+ /* dump the packet for debugging */
+ if (omaprpc_debug & OMAPRPC_ZONE_VERBOSE) {
+ print_hex_dump(KERN_DEBUG, "OMAPRPC: TX: ",
+ DUMP_PREFIX_NONE, 16, 1, kbuf, use, true);
+ omaprpc_print_msg(rpc, "TX:", kbuf);
+ }
+
+ if (omaprpc_debug & OMAPRPC_ZONE_PERF) {
+ /* capture the time delay between write and callback */
+ do_gettimeofday(&start_time);
+ }
+
+ /* Send the msg */
+ ret = rpmsg_send_offchannel(rpcserv->rpdev,
+ rpc->ept->addr, rpc->dst, kbuf, use);
+ if (ret) {
+ OMAPRPC_ERR(rpcserv->dev,
+ "OMAPRPC: rpmsg_send failed: %d\n", ret);
+ /* remove the function data that we just saved */
+ omaprpc_fxn_get(rpc, rpc->msgId);
+ /* unwind */
+ omaprpc_xlate_buffers(rpc, function, OMAPRPC_RPA_TO_UVA);
+ goto failure;
+ }
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpcserv->dev,
+ "OMAPRPC: ==> Sent msg to remote endpoint %u\n",
+ rpc->dst);
+failure:
+ if (ret >= 0)
+ ret = len;
+ else
+ kfree(function);
+
+ /* return the length of the data written to us */
+ return ret;
+}
+
+static unsigned int omaprpc_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct omaprpc_instance_t *rpc = filp->private_data;
+ unsigned int mask = 0;
+
+ /* grab the mutex so we can check the queue */
+ if (mutex_lock_interruptible(&rpc->lock))
+ return -ERESTARTSYS;
+
+ /* Wait for items to enter the queue */
+ poll_wait(filp, &rpc->readq, wait);
+ if (rpc->state == OMAPRPC_STATE_FAULT) {
+ mutex_unlock(&rpc->lock);
+ return -ENXIO; /* The remote service died somehow */
+ }
+
+ /* if the queue is not empty set the poll bit correctly */
+ if (!skb_queue_empty(&rpc->queue))
+ mask |= (POLLIN | POLLRDNORM);
+
+ /* @TODO: implement missing rpmsg virtio functionality here */
+ if (true)
+ mask |= POLLOUT | POLLWRNORM;
+
+ mutex_unlock(&rpc->lock);
+
+ return mask;
+}
+
+static const struct file_operations omaprpc_fops = {
+ .owner = THIS_MODULE,
+ .open = omaprpc_open,
+ .release = omaprpc_release,
+ .unlocked_ioctl = omaprpc_ioctl,
+ .read = omaprpc_read,
+ .write = omaprpc_write,
+ .poll = omaprpc_poll,
+};
+
+static int omaprpc_device_create(struct rpmsg_channel *rpdev)
+{
+ char kbuf[512];
+ struct omaprpc_msg_header_t *hdr =
+ (struct omaprpc_msg_header_t *)&kbuf[0];
+ int ret = 0;
+ u32 len = 0;
+
+ /* Initialize the Connection Request Message */
+ hdr->msg_type = OMAPRPC_MSG_QUERY_CHAN_INFO;
+ hdr->msg_len = 0;
+ len = sizeof(struct omaprpc_msg_header_t);
+
+ /* The device will be created during the reply */
+ ret = rpmsg_send(rpdev, (char *)kbuf, len);
+ if (ret) {
+ dev_err(&rpdev->dev, "OMAPRPC: rpmsg_send failed: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int omaprpc_probe(struct rpmsg_channel *rpdev)
+{
+ int ret, major, minor;
+ struct omaprpc_service_t *rpcserv = NULL, *tmp;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, &rpdev->dev,
+ "OMAPRPC: Probing service with src %u dst %u\n",
+ rpdev->src, rpdev->dst);
+
+again: /* SMP systems could race device probes */
+
+ /* allocate the memory for an integer ID */
+ if (!idr_pre_get(&omaprpc_services, GFP_KERNEL)) {
+ OMAPRPC_ERR(&rpdev->dev, "OMAPRPC: idr_pre_get failed\n");
+ return -ENOMEM;
+ }
+
+ /* dynamically assign a new minor number */
+ spin_lock(&omaprpc_services_lock);
+ ret = idr_get_new(&omaprpc_services, rpcserv, &minor);
+ if (ret == -EAGAIN) {
+ spin_unlock(&omaprpc_services_lock);
+ OMAPRPC_ERR(&rpdev->dev,
+ "OMAPRPC: Race to the new idr memory! (ret=%d)\n",
+ ret);
+ goto again;
+ } else if (ret != 0) { /* probably -ENOSPC */
+ spin_unlock(&omaprpc_services_lock);
+ OMAPRPC_ERR(&rpdev->dev,
+ "OMAPRPC: failed to idr_get_new: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * look for an already created rpc service
+ * and use that if the minor number matches
+ */
+ list_for_each_entry(tmp, &omaprpc_services_list, list) {
+ if (tmp->minor == minor) {
+ rpcserv = tmp;
+ idr_replace(&omaprpc_services, rpcserv, minor);
+ break;
+ }
+ }
+ spin_unlock(&omaprpc_services_lock);
+
+ /* if we replaced a device, skip the creation */
+ if (rpcserv)
+ goto serv_up;
+
+ /* Create a new character device and add it to the kernel /dev fs */
+ rpcserv = kzalloc(sizeof(*rpcserv), GFP_KERNEL);
+ if (!rpcserv) {
+ OMAPRPC_ERR(&rpdev->dev, "OMAPRPC: kzalloc failed\n");
+ ret = -ENOMEM;
+ goto rem_idr;
+ }
+
+ /*
+ * Replace the pointer for the minor number, it shouldn't have existed
+ * (or was associated with NULL previously) so this is really an
+ * assignment
+ */
+ spin_lock(&omaprpc_services_lock);
+ idr_replace(&omaprpc_services, rpcserv, minor);
+ spin_unlock(&omaprpc_services_lock);
+
+ /* Initialize the instance list in the service */
+ INIT_LIST_HEAD(&rpcserv->instance_list);
+
+ /* Initialize the Mutex */
+ mutex_init(&rpcserv->lock);
+
+ /* Initialize the Completion lock */
+ init_completion(&rpcserv->comp);
+
+ /* Add this service to the list of services */
+ list_add(&rpcserv->list, &omaprpc_services_list);
+
+ /* get the assigned major number from the dev_t */
+ major = MAJOR(omaprpc_dev);
+
+ /* Create the character device */
+ cdev_init(&rpcserv->cdev, &omaprpc_fops);
+ rpcserv->cdev.owner = THIS_MODULE;
+
+ /* Add the character device to the kernel */
+ ret = cdev_add(&rpcserv->cdev, MKDEV(major, minor), 1);
+ if (ret) {
+ OMAPRPC_ERR(&rpdev->dev, "OMAPRPC: cdev_add failed: %d\n", ret);
+ goto free_rpc;
+ }
+
+ ret = omaprpc_device_create(rpdev);
+ if (ret) {
+ OMAPRPC_ERR(&rpdev->dev,
+ "OMAPRPC: failed to query channel info: %d\n", ret);
+ goto clean_cdev;
+ }
+
+serv_up:
+ rpcserv->rpdev = rpdev;
+ rpcserv->minor = minor;
+ rpcserv->state = OMAPRPC_SERVICE_STATE_UP;
+
+ /*
+ * Associate the service with the sysfs
+ * entry, this will be allow container_of to get the service pointer
+ */
+ dev_set_drvdata(&rpdev->dev, rpcserv);
+
+ /* Signal that the driver setup is complete */
+ complete_all(&rpcserv->comp);
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, &rpdev->dev,
+ "OMAPRPC: new RPC connection srv channel: %u -> %u!\n",
+ rpdev->src, rpdev->dst);
+ return 0;
+
+clean_cdev:
+ /* Failed to create sysfs entry, delete character device */
+ cdev_del(&rpcserv->cdev);
+free_rpc:
+ /* Delete the allocated memory for the service */
+ kfree(rpcserv);
+rem_idr:
+ /* Remove the minor number from our integer ID pool */
+ spin_lock(&omaprpc_services_lock);
+ idr_remove(&omaprpc_services, minor);
+ spin_unlock(&omaprpc_services_lock);
+ /* Return the set error */
+ return ret;
+}
+
+static void __devexit omaprpc_remove(struct rpmsg_channel *rpdev)
+{
+ struct omaprpc_service_t *rpcserv = dev_get_drvdata(&rpdev->dev);
+ int major = MAJOR(omaprpc_dev);
+ struct omaprpc_instance_t *rpc = NULL;
+
+ if (rpcserv == NULL) {
+ OMAPRPC_ERR(&rpdev->dev, "Service was NULL\n");
+ return;
+ }
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpcserv->dev,
+ "OMAPRPC: removing rpmsg omaprpc driver %u.%u\n",
+ major, rpcserv->minor);
+
+ spin_lock(&omaprpc_services_lock);
+ idr_remove(&omaprpc_services, rpcserv->minor);
+ spin_unlock(&omaprpc_services_lock);
+
+ mutex_lock(&rpcserv->lock);
+
+ /* If there are no instances in the list, just teardown */
+ if (list_empty(&rpcserv->instance_list)) {
+ device_destroy(omaprpc_class, MKDEV(major, rpcserv->minor));
+ cdev_del(&rpcserv->cdev);
+ list_del(&rpcserv->list);
+ mutex_unlock(&rpcserv->lock);
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, &rpdev->dev,
+ "OMAPRPC: no instances, removed driver!\n");
+ kfree(rpcserv);
+ return;
+ }
+
+ /*
+ * If there are rpc instances that means that this is a recovery
+ * operation. Don't clean the rpcserv. Each instance may be in a
+ * weird state.
+ */
+ init_completion(&rpcserv->comp);
+ rpcserv->state = OMAPRPC_SERVICE_STATE_DOWN;
+ list_for_each_entry(rpc, &rpcserv->instance_list, list) {
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpcserv->dev,
+ "Instance %p in state %d\n", rpc, rpc->state);
+ /* set rpc instance to fault state */
+ rpc->state = OMAPRPC_STATE_FAULT;
+ /* complete any on-going transactions */
+ if ((rpc->state == OMAPRPC_STATE_CONNECTED ||
+ rpc->state == OMAPRPC_STATE_DISCONNECTED) &&
+ rpc->transisioning) {
+ /*
+ * we were in the middle of
+ * connecting or disconnecting
+ */
+ complete_all(&rpc->reply_arrived);
+ }
+ /* wake up anyone waiting on a read */
+ wake_up_interruptible(&rpc->readq);
+ }
+ mutex_unlock(&rpcserv->lock);
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, &rpdev->dev,
+ "OMAPRPC: removed rpmsg omaprpc driver.\n");
+}
+
+static void omaprpc_driver_cb(struct rpmsg_channel *rpdev,
+ void *data, int len, void *priv, u32 src)
+{
+ struct omaprpc_service_t *rpcserv = dev_get_drvdata(&rpdev->dev);
+
+ struct omaprpc_msg_header_t *hdr = data;
+ struct omaprpc_channel_info_t *info;
+ char *buf = (char *)data;
+ u32 expected = 0;
+ int major = 0;
+
+ expected = sizeof(struct omaprpc_msg_header_t);
+ switch (hdr->msg_type) {
+ case OMAPRPC_MSG_CHAN_INFO:
+ expected += sizeof(struct omaprpc_channel_info_t);
+ break;
+ }
+
+ if (len < expected) {
+ OMAPRPC_ERR(&rpdev->dev,
+ "OMAPRPC driver: truncated message detected! "
+ "Was %u bytes long, expected %u\n", len, expected);
+ return;
+ }
+
+ switch (hdr->msg_type) {
+ case OMAPRPC_MSG_CHAN_INFO:
+ major = MAJOR(omaprpc_dev);
+ info = OMAPRPC_PAYLOAD(buf, omaprpc_channel_info_t);
+ info->name[sizeof(info->name) - 1] = '\0';
+
+ if (rpcserv->dev) {
+ OMAPRPC_ERR(&rpdev->dev,
+ "OMAPRPC: device already created!\n");
+ break;
+ }
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, &rpdev->dev,
+ "OMAPRPC: creating device: %s\n",
+ info->name);
+ /* Create the /dev sysfs entry */
+ rpcserv->dev = device_create(omaprpc_class, &rpdev->dev,
+ MKDEV(major,
+ rpcserv->minor),
+ NULL, info->name);
+ if (IS_ERR(rpcserv->dev)) {
+ int ret = PTR_ERR(rpcserv->dev);
+ dev_err(&rpdev->dev,
+ "OMAPRPC: device_create failed: %d\n",
+ ret);
+ /* TODO:
+ * is this cleanup enough?
+ * At this point probe has succeded
+ */
+ /*
+ * Failed to create sysfs entry, delete
+ * character device
+ */
+ cdev_del(&rpcserv->cdev);
+ dev_set_drvdata(&rpdev->dev, NULL);
+ /*
+ * Remove the minor number from our integer
+ * ID pool
+ */
+ spin_lock(&omaprpc_services_lock);
+ idr_remove(&omaprpc_services, rpcserv->minor);
+ spin_unlock(&omaprpc_services_lock);
+ /* Delete the allocated memory for the service
+ */
+ kfree(rpcserv);
+ }
+ break;
+ default:
+ OMAPRPC_ERR(&rpdev->dev, "OMAPRPC: Unrecognized code %u\n",
+ hdr->msg_type);
+ break;
+ }
+}
+
+static struct rpmsg_device_id omaprpc_id_table[] = {
+ {.name = "omaprpc"},
+ {},
+};
+
+MODULE_DEVICE_TABLE(rpmsg, omaprpc_id_table);
+
+static struct rpmsg_driver omaprpc_driver = {
+ .drv.name = KBUILD_MODNAME,
+ .drv.owner = THIS_MODULE,
+ .id_table = omaprpc_id_table,
+ .probe = omaprpc_probe,
+ .remove = __devexit_p(omaprpc_remove),
+ .callback = omaprpc_driver_cb,
+};
+
+static int __init omaprpc_init(void)
+{
+ int ret;
+
+ ret = alloc_chrdev_region(&omaprpc_dev, 0, OMAPRPC_CORE_REMOTE_MAX,
+ KBUILD_MODNAME);
+ if (ret) {
+ pr_err("OMAPRPC: alloc_chrdev_region failed: %d\n", ret);
+ return ret;
+ }
+
+ omaprpc_class = class_create(THIS_MODULE, KBUILD_MODNAME);
+ if (IS_ERR(omaprpc_class)) {
+ ret = PTR_ERR(omaprpc_class);
+ pr_err("OMAPRPC: class_create failed: %d\n", ret);
+ goto unreg_region;
+ }
+
+ ret = register_rpmsg_driver(&omaprpc_driver);
+ pr_err("OMAPRPC: Registration of OMAPRPC rpmsg service returned %d! ",
+ ret);
+ pr_err("OMAPRPC: debug=%d\n", omaprpc_debug);
+ return ret;
+unreg_region:
+ unregister_chrdev_region(omaprpc_dev, OMAPRPC_CORE_REMOTE_MAX);
+ return ret;
+}
+
+module_init(omaprpc_init);
+
+static void __exit omaprpc_fini(void)
+{
+ struct omaprpc_service_t *rpcserv, *tmp;
+ int major = MAJOR(omaprpc_dev);
+
+ unregister_rpmsg_driver(&omaprpc_driver);
+ list_for_each_entry_safe(rpcserv, tmp, &omaprpc_services_list, list) {
+ device_destroy(omaprpc_class, MKDEV(major, rpcserv->minor));
+ cdev_del(&rpcserv->cdev);
+ list_del(&rpcserv->list);
+ kfree(rpcserv);
+ }
+ class_destroy(omaprpc_class);
+ unregister_chrdev_region(omaprpc_dev, OMAPRPC_CORE_REMOTE_MAX);
+}
+
+module_exit(omaprpc_fini);
+
+MODULE_AUTHOR("Erik Rainey <erik.rainey@ti.com>");
+MODULE_DESCRIPTION("OMAP Remote Procedure Call Driver");
+MODULE_ALIAS("rpmsg:omaprpc");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rpmsg/omaprpc/omap_rpc_dmabuf.c b/drivers/rpmsg/omaprpc/omap_rpc_dmabuf.c
new file mode 100644
index 0000000..7f2a79f
--- /dev/null
+++ b/drivers/rpmsg/omaprpc/omap_rpc_dmabuf.c
@@ -0,0 +1,449 @@
+/*
+ * OMAP Remote Procedure Call Driver.
+ *
+ * Copyright(c) 2012 Texas Instruments. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "omap_rpc_internal.h"
+
+static void omaprpc_dma_clear(struct omaprpc_instance_t *rpc)
+{
+ struct dma_info_t *pos, *n;
+ mutex_lock(&rpc->lock);
+ list_for_each_entry_safe(pos, n, &rpc->dma_list, list) {
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "Removing Pinning for FD %u\n", pos->fd);
+ list_del((struct list_head *)pos);
+ dma_buf_unmap_attachment(pos->attach, pos->sgt,
+ DMA_BIDIRECTIONAL);
+ dma_buf_detach(pos->dbuf, pos->attach);
+ dma_buf_put(pos->dbuf);
+ kfree(pos);
+ }
+ mutex_unlock(&rpc->lock);
+ return;
+}
+
+static int omaprpc_dma_add(struct omaprpc_instance_t *rpc,
+ struct dma_info_t *dma)
+{
+ if (dma) {
+ mutex_lock(&rpc->lock);
+ list_add(&dma->list, &rpc->dma_list);
+ mutex_unlock(&rpc->lock);
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev,
+ "Added FD %u to list", dma->fd);
+ }
+ return 0;
+}
+
+static phys_addr_t omaprpc_pin_buffer(struct omaprpc_instance_t *rpc,
+ void *reserved)
+{
+ struct dma_info_t *dma = kmalloc(sizeof(struct dma_info_t), GFP_KERNEL);
+ if (dma == NULL)
+ return 0;
+
+ dma->fd = (int)reserved;
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev, "Pining with FD %u\n", dma->fd);
+ dma->dbuf = dma_buf_get((int)reserved);
+ if (!(IS_ERR(dma->dbuf))) {
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev, "DMA_BUF=%p\n", dma->dbuf);
+ dma->attach = dma_buf_attach(dma->dbuf, rpc->rpcserv->dev);
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev, "attach=%p\n", dma->attach);
+ dma->sgt = dma_buf_map_attachment(dma->attach,
+ DMA_BIDIRECTIONAL);
+ omaprpc_dma_add(rpc, dma);
+ return sg_dma_address(dma->sgt->sgl);
+ } else
+ kfree(dma);
+ return 0;
+}
+
+static phys_addr_t omaprpc_dma_find(struct omaprpc_instance_t *rpc,
+ void *reserved)
+{
+ phys_addr_t addr = 0;
+ struct list_head *pos = NULL;
+ struct dma_info_t *node = NULL;
+ int fd = (int)reserved;
+ mutex_lock(&rpc->lock);
+ list_for_each(pos, &rpc->dma_list) {
+ node = (struct dma_info_t *)pos;
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "Looking for FD %u, found FD %u\n", fd, node->fd);
+ if (node->fd == fd) {
+ addr = sg_dma_address(node->sgt->sgl);
+ break;
+ }
+ }
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "Returning Addr %p for FD %u\n", (void *)addr, fd);
+ mutex_unlock(&rpc->lock);
+ return addr;
+}
+
+phys_addr_t omaprpc_buffer_lookup(struct omaprpc_instance_t *rpc,
+ uint32_t core,
+ virt_addr_t uva,
+ virt_addr_t buva,
+ void *reserved)
+{
+ phys_addr_t lpa = 0, rpa = 0;
+ /* User VA - Base User VA = User Offset assuming not tiler 2D */
+ /* For Tiler2D offset is corrected later */
+ long uoff = uva - buva;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "CORE=%u BUVA=%p UVA=%p Uoff=%ld [0x%016lx] Hdl=%p\n",
+ core, (void *)buva, (void *)uva, uoff, (ulong) uoff,
+ reserved);
+
+ if (uoff < 0) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Offsets calculation for BUVA=%p from UVA=%p is a "
+ "negative number. Bad parameters!\n",
+ (void *)buva, (void *)uva);
+ rpa = 0;
+ } else {
+ /* find the base of the dma buf from the list */
+ lpa = omaprpc_dma_find(rpc, reserved);
+ if (lpa == 0) {
+ /* wasn't in the list, convert the pointer */
+ lpa = omaprpc_pin_buffer(rpc, reserved);
+ }
+
+ /* recalculate the offset in the user buffer
+ (accounts for tiler 2D) */
+ uoff = omaprpc_recalc_off(lpa, uoff);
+
+ /* offset the lpa by the offset in the user buffer */
+ lpa += uoff;
+
+ /* convert the local physical address to remote physical
+ address */
+ rpa = rpmsg_local_to_remote_pa(core, lpa);
+ }
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "ARM VA %p == ARM PA %p => REMOTE[%u] PA %p (RESV %p)\n",
+ (void *)uva, (void *)lpa, core, (void *)rpa, reserved);
+ return rpa;
+}
+
+int omaprpc_xlate_buffers(struct omaprpc_instance_t *rpc,
+ struct omaprpc_call_function_t *function,
+ int direction)
+{
+ int idx = 0, start = 0, inc = 1, limit = 0, ret = 0;
+ uint32_t ptr_idx = 0, pri_offset = 0, sec_offset = 0, pg_offset = 0,
+ size = 0;
+
+ /* @NOTE not all the parameters are pointers so this may be sparse */
+ uint8_t *base_ptrs[OMAPRPC_MAX_PARAMETERS];
+ struct dma_buf *dbufs[OMAPRPC_MAX_PARAMETERS];
+
+ if (function->num_translations == 0)
+ return 0;
+
+ limit = function->num_translations;
+ memset(base_ptrs, 0, sizeof(base_ptrs));
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "Operating on %d pointers\n", function->num_translations);
+ /* we may have a failure during translation, in which case we need to
+ unwind the whole operation from here */
+
+ for (idx = start; idx != limit; idx += inc) {
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "#### Starting Translation %d of %d by %d\n",
+ idx, limit, inc);
+ /* conveinence variables */
+ ptr_idx = function->translations[idx].index;
+ sec_offset = function->translations[idx].offset;
+
+ /* if the pointer index for this translation is invalid */
+ if (ptr_idx >= OMAPRPC_MAX_PARAMETERS) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Invalid parameter pointer index %u\n",
+ ptr_idx);
+ goto unwind;
+ } else if (function->params[ptr_idx].type !=
+ OMAPRPC_PARAM_TYPE_PTR) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Parameter index %u is not a pointer "
+ "(type %u)\n",
+ ptr_idx, function->params[ptr_idx].type);
+ goto unwind;
+ }
+
+ size = function->params[ptr_idx].size;
+
+ if (sec_offset >= (size - sizeof(virt_addr_t))) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Offset is larger than data area! "
+ "(sec_offset=%u size=%u)\n", sec_offset,
+ size);
+ goto unwind;
+ }
+
+ if (function->params[ptr_idx].data == 0) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Supplied user pointer is NULL!\n");
+ goto unwind;
+ }
+
+ /* if the KVA pointer has not been mapped */
+ if (base_ptrs[ptr_idx] == NULL) {
+ size_t start = (pri_offset + sec_offset) & PAGE_MASK;
+ size_t end = PAGE_SIZE;
+ int ret = 0;
+
+ /* compute the secondary offset */
+ pri_offset = function->params[ptr_idx].data -
+ function->params[ptr_idx].base;
+
+ /* acquire a handle to the dma buf */
+ dbufs[ptr_idx] = dma_buf_get((int)function->
+ params[ptr_idx].reserved);
+
+ /* map the dma buf into cpu memory? */
+ ret = dma_buf_begin_cpu_access(dbufs[ptr_idx],
+ start,
+ end, DMA_BIDIRECTIONAL);
+ if (ret < 0) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "OMAPRPC: Failed to acquire cpu access "
+ "to the DMA Buf! ret=%d\n", ret);
+ dma_buf_put(dbufs[ptr_idx]);
+ goto unwind;
+ }
+
+ /* caluculate the base pointer for the region. */
+ base_ptrs[ptr_idx] = dma_buf_kmap(dbufs[ptr_idx],
+ ((pri_offset +
+ sec_offset) >>
+ PAGE_SHIFT));
+
+ /* calculate the new offset within that page */
+ pg_offset =
+ ((pri_offset + sec_offset) & (PAGE_SIZE - 1));
+
+ if (base_ptrs[ptr_idx] != NULL) {
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev,
+ "KMap'd base_ptr[%u]=%p dbuf=%p into "
+ "kernel from %zu for %zu bytes, "
+ "PG_OFFSET=%u\n",
+ ptr_idx,
+ base_ptrs[ptr_idx],
+ dbufs[ptr_idx],
+ start, end, pg_offset);
+ }
+ }
+
+ /* if the KVA pointer is not NULL */
+ if (base_ptrs[ptr_idx] != NULL) {
+ if (direction == OMAPRPC_UVA_TO_RPA) {
+ /* get the kernel virtual pointer to the
+ pointer to translate */
+ virt_addr_t kva =
+ (virt_addr_t) &
+ ((base_ptrs[ptr_idx])[pg_offset]);
+ virt_addr_t uva = 0;
+ virt_addr_t buva =
+ (virt_addr_t) function->translations[idx].
+ base;
+ phys_addr_t rpa = 0;
+ void *reserved =
+ (void *)function->translations[idx].
+ reserved;
+
+ /* make sure we won't cause an unalign mem
+ access */
+ if ((kva & 0x3) > 0) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "ERROR: KVA %p is unaligned!\n",
+ (void *)kva);
+ return -EADDRNOTAVAIL;
+ }
+ /* load the user's VA */
+ uva = *(virt_addr_t *) kva;
+ if (uva == 0) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "ERROR: Failed to access user "
+ "buffer to translate pointer"
+ "\n");
+ print_hex_dump(KERN_DEBUG,
+ "OMAPRPC: KMAP: ",
+ DUMP_PREFIX_NONE, 16, 1,
+ base_ptrs[ptr_idx],
+ PAGE_SIZE, true);
+ goto unwind;
+ }
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev,
+ "Replacing UVA %p at KVA %p PTRIDX:%u "
+ "PG_OFFSET:%u IDX:%d RESV:%p\n",
+ (void *)uva, (void *)kva, ptr_idx,
+ pg_offset, idx, reserved);
+
+ /* calc the new RPA (remote physical address) */
+ rpa = omaprpc_buffer_lookup(rpc, rpc->core,
+ uva, buva,
+ reserved);
+ /* save the old value */
+ function->translations[idx].reserved =
+ (size_t) uva;
+ /* replace with new RPA */
+ *(phys_addr_t *) kva = rpa;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev,
+ "Replaced UVA %p with RPA %p at KVA %p\n",
+ (void *)uva, (void *)rpa,
+ (void *)kva);
+
+ if (rpa == 0) {
+ /* need to unwind all operations.. */
+ direction = OMAPRPC_RPA_TO_UVA;
+ start = idx - 1;
+ inc = -1;
+ limit = -1;
+ ret = -ENODATA;
+ /*
+ * TODO: unmap the parameter
+ * base pointer
+ */
+ goto restart;
+ }
+ } else if (direction == OMAPRPC_RPA_TO_UVA) {
+ /* address of the pointer in memory */
+ virt_addr_t kva = 0;
+ virt_addr_t uva = 0;
+ phys_addr_t rpa = 0;
+ kva = (virt_addr_t)
+ &((base_ptrs[ptr_idx])[pg_offset]);
+ /*
+ * make sure we won't cause an
+ * unalign mem access
+ */
+ if ((kva & 0x3) > 0)
+ return -EADDRNOTAVAIL;
+ /* get what was there for debugging */
+ rpa = *(phys_addr_t *) kva;
+ /* convienence value of uva */
+ uva = (virt_addr_t)
+ function->translations[idx].reserved;
+ /*
+ * replace the translated value with the
+ * remember version
+ */
+ *(virt_addr_t *) kva = uva;
+
+ /*
+ * TODO: DMA_BUF requires unmapping the
+ * data from the TILER.
+ */
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev,
+ "Replaced RPA %p with UVA %p at KVA %p\n",
+ (void *)rpa, (void *)uva,
+ (void *)kva);
+
+ if (uva == 0) {
+ /* need to unwind all operations.. */
+ direction = OMAPRPC_RPA_TO_UVA;
+ start = idx - 1;
+ inc = -1;
+ limit = -1;
+ ret = -ENODATA;
+ /* @TODO unmap the parameter base
+ pointer */
+ goto restart;
+ }
+ }
+ } else {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Failed to map UVA to KVA "
+ "to do translation!\n");
+ /*
+ * we can arrive here from multiple points, but the
+ * action is the same from everywhere
+ */
+unwind:
+ if (direction == OMAPRPC_UVA_TO_RPA) {
+ /*
+ * we've encountered an error which needs to
+ * unwind all the operations
+ */
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Unwinding UVA to RPA translations!\n");
+ direction = OMAPRPC_RPA_TO_UVA;
+ start = idx - 1;
+ inc = -1;
+ limit = -1;
+ ret = -ENOBUFS;
+ goto restart;
+ } else if (direction == OMAPRPC_RPA_TO_UVA) {
+ /*
+ * there was a problem restoring the pointer,
+ * there's nothing to do but to continue
+ * processing
+ */
+ continue;
+ }
+ }
+restart:
+ if (base_ptrs[ptr_idx]) {
+ size_t start = (pri_offset + sec_offset) & PAGE_MASK;
+ size_t end = PAGE_SIZE;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "Unkmaping base_ptrs[%u]=%p from dbuf=%p %zu "
+ "for %zu bytes\n",
+ ptr_idx,
+ base_ptrs[ptr_idx],
+ dbufs[ptr_idx], start, end);
+ /*
+ * unmap the page in case this pointer needs to
+ * move to a different adddress
+ */
+ dma_buf_kunmap(dbufs[ptr_idx],
+ ((pri_offset +
+ sec_offset) >> PAGE_SHIFT),
+ base_ptrs[ptr_idx]);
+ /* end access to this page */
+ dma_buf_end_cpu_access(dbufs[ptr_idx],
+ start, end, DMA_BIDIRECTIONAL);
+ base_ptrs[ptr_idx] = NULL;
+ dma_buf_put(dbufs[ptr_idx]);
+ dbufs[ptr_idx] = NULL;
+ pg_offset = 0;
+ }
+ }
+ if (direction == OMAPRPC_RPA_TO_UVA) {
+ /*
+ * we're done translating everything, unpin all the translations
+ * and the parameters
+ */
+ omaprpc_dma_clear(rpc);
+ }
+ return ret;
+}
diff --git a/drivers/rpmsg/omaprpc/omap_rpc_internal.h b/drivers/rpmsg/omaprpc/omap_rpc_internal.h
new file mode 100644
index 0000000..5f32c36
--- /dev/null
+++ b/drivers/rpmsg/omaprpc/omap_rpc_internal.h
@@ -0,0 +1,215 @@
+/*
+ * OMAP Remote Procedure Call Driver.
+ *
+ * Copyright(c) 2012 Texas Instruments. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _OMAP_RPC_INTERNAL_H_
+#define _OMAP_RPC_INTERNAL_H_
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/cdev.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/skbuff.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/remoteproc.h>
+
+#if defined(CONFIG_RPMSG) || defined(CONFIG_RPMSG_MODULE)
+#include <linux/rpmsg.h>
+#else
+#error "OMAP RPC requires RPMSG"
+#endif
+
+#if defined(CONFIG_RPC_OMAP) || defined(CONFIG_RPC_OMAP_MODULE)
+#include <linux/omap_rpc.h>
+#endif
+
+#if defined(CONFIG_TI_TILER) || defined(CONFIG_TI_TILER_MODULE)
+#include <mach/tiler.h>
+#endif
+
+#if defined(CONFIG_DMA_SHARED_BUFFER) \
+|| defined(CONFIG_DMA_SHARED_BUFFER_MODULE)
+#include <linux/dma-buf.h>
+#endif
+
+#if defined(CONFIG_ION_OMAP) || defined(CONFIG_ION_OMAP_MODULE)
+#include <linux/omap_ion.h>
+extern struct ion_device *omap_ion_device;
+#if defined(CONFIG_PVR_SGX) || defined(CONFIG_PVR_SGX_MODULE)
+#include "../../gpu/pvr/ion.h"
+#endif
+#endif
+
+#if defined(CONFIG_TI_TILER) || defined(CONFIG_TI_TILER_MODULE)
+#define OMAPRPC_USE_TILER
+#else
+#undef OMAPRPC_USE_TILER
+#endif
+
+#if defined(CONFIG_ION_OMAP) || defined(CONFIG_ION_OMAP_MODULE)
+#define OMAPRPC_USE_ION
+#undef OMAPRPC_USE_DMABUF
+#define OMAPRPC_USE_RPROC_LOOKUP /* android supports this. */
+#if defined(CONFIG_PVR_SGX) || defined(CONFIG_PVR_SGX_MODULE)
+#define OMAPRPC_USE_PVR
+#else
+#undef OMAPRPC_USE_PVR
+#endif
+#elif defined(CONFIG_DMA_SHARED_BUFFER) \
+|| defined(CONFIG_DMA_SHARED_BUFFER_MODULE)
+#undef OMAPRPC_USE_ION
+#define OMAPRPC_USE_DMABUF
+/* genernic linux does not support it. */
+#undef OMAPRPC_USE_RPROC_LOOKUP
+#else
+#error "OMAP RPC requires either ION_OMAP or DMA_SHARED_BUFFER"
+#endif
+
+#define OMAPRPC_ZONE_INFO (0x1)
+#define OMAPRPC_ZONE_PERF (0x2)
+#define OMAPRPC_ZONE_VERBOSE (0x4)
+
+extern unsigned int omaprpc_debug;
+
+#define OMAPRPC_PRINT(flag, dev, fmt, ...) { \
+ if ((flag) & omaprpc_debug) \
+ dev_info((dev), (fmt), ## __VA_ARGS__); \
+}
+
+#define OMAPRPC_ERR(dev, fmt, ...) dev_err((dev), (fmt), ## __VA_ARGS__)
+
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+typedef u64 virt_addr_t;
+#else
+typedef u32 virt_addr_t;
+#endif
+
+enum omaprpc_service_state_e {
+ OMAPRPC_SERVICE_STATE_DOWN,
+ OMAPRPC_SERVICE_STATE_UP,
+};
+
+/**
+ * struct omaprpc_service_t - The per-service (endpoint) data. Contains the
+ * list of instances.
+ */
+struct omaprpc_service_t {
+ struct list_head list;
+ struct cdev cdev;
+ struct device *dev;
+ struct rpmsg_channel *rpdev;
+ int minor;
+ struct list_head instance_list;
+ struct mutex lock;
+ struct completion comp;
+ int state;
+#if defined(OMAPRPC_USE_ION)
+ struct ion_client *ion_client;
+#endif
+};
+
+/**
+ * struct omaprpc_call_function_list_t - The list of all outstanding function
+ * calls in this instance.
+ */
+struct omaprpc_call_function_list_t {
+ struct list_head list;
+ struct omaprpc_call_function_t *function;
+ u16 msgId;
+};
+
+/**
+ * struct omaprpc_instance_t - The per-instance data structure (per user).
+ */
+struct omaprpc_instance_t {
+ struct list_head list;
+ struct omaprpc_service_t *rpcserv;
+ struct sk_buff_head queue;
+ struct mutex lock;
+ wait_queue_head_t readq;
+ struct completion reply_arrived;
+ struct rpmsg_endpoint *ept;
+ int transisioning;
+ u32 dst;
+ int state;
+ u32 core;
+#if defined(OMAPRPC_USE_ION)
+ struct ion_client *ion_client;
+#elif defined(OMAPRPC_USE_DMABUF)
+ struct list_head dma_list;
+#endif
+ u16 msgId;
+ struct list_head fxn_list;
+};
+
+#if defined(OMAPRPC_USE_DMABUF)
+/**
+ * struct dma_info_t - The DMA Info structure tracks the dma_buf relevant
+ * variables.
+ */
+struct dma_info_t {
+ struct list_head list;
+ int fd;
+ struct dma_buf *dbuf;
+ struct dma_buf_attachment *attach;
+ struct sg_table *sgt;
+};
+#endif
+
+/*!
+ * A wrapper function to translate local physical addresses to the remote core
+ * memory maps. Initialially we can only use an internal static table until
+ * rproc support querying.
+ */
+#if defined(OMAPRPC_USE_RPROC_LOOKUP)
+phys_addr_t rpmsg_local_to_remote_pa(struct omaprpc_instance_t *rpc,
+ phys_addr_t pa);
+#else
+phys_addr_t rpmsg_local_to_remote_pa(uint32_t core, phys_addr_t pa);
+#endif
+
+/*!
+ * This function translates all the pointers within the function call
+ * structure and the translation structures.
+ */
+int omaprpc_xlate_buffers(struct omaprpc_instance_t *rpc,
+ struct omaprpc_call_function_t *function,
+ int direction);
+
+/*!
+ * Converts a buffer to a remote core address.
+ */
+phys_addr_t omaprpc_buffer_lookup(struct omaprpc_instance_t *rpc,
+ uint32_t core,
+ virt_addr_t uva,
+ virt_addr_t buva, void *reserved);
+
+/*!
+ * Used to recalculate the offset of a buffer and handles cases where Tiler
+ * 2d regions are concerned.
+ */
+long omaprpc_recalc_off(phys_addr_t lpa, long uoff);
+
+#endif
diff --git a/drivers/rpmsg/omaprpc/omap_rpc_ion.c b/drivers/rpmsg/omaprpc/omap_rpc_ion.c
new file mode 100644
index 0000000..6354a4a
--- /dev/null
+++ b/drivers/rpmsg/omaprpc/omap_rpc_ion.c
@@ -0,0 +1,382 @@
+/*
+ * OMAP Remote Procedure Call Driver.
+ *
+ * Copyright(c) 2012 Texas Instruments. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "omap_rpc_internal.h"
+
+static uint8_t *omaprpc_map_parameter(struct omaprpc_instance_t *rpc,
+ struct omaprpc_param_t *param)
+{
+ uint32_t pri_offset = 0;
+ uint8_t *kva = NULL;
+ uint8_t *bkva = NULL;
+
+ /* calc any primary offset if present */
+ pri_offset = param->data - param->base;
+
+ bkva = (uint8_t *) ion_map_kernel(rpc->ion_client,
+ (struct ion_handle *)param->reserved);
+
+ /*
+ * set the kernel VA equal to the base kernel
+ * VA plus the primary offset
+ */
+ kva = &bkva[pri_offset];
+
+ /*
+ * in ION case, secondary offset is ignored here
+ * because the entire region is mapped.
+ */
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "Mapped UVA:%p to KVA:%p+OFF:%08x SIZE:%08x "
+ "(MKVA:%p to END:%p)\n",
+ (void *)param->data,
+ (void *)kva, pri_offset, param->size,
+ (void *)bkva, (void *)&bkva[param->size]);
+
+ return kva;
+
+}
+
+static void omaprpc_unmap_parameter(struct omaprpc_instance_t *rpc,
+ struct omaprpc_param_t *param,
+ uint8_t *ptr, uint32_t sec_offset)
+{
+ ion_unmap_kernel(rpc->ion_client, (struct ion_handle *)param->reserved);
+}
+
+phys_addr_t omaprpc_buffer_lookup(struct omaprpc_instance_t *rpc,
+ uint32_t core, virt_addr_t uva,
+ virt_addr_t buva, void *reserved)
+{
+ phys_addr_t lpa = 0, rpa = 0;
+ /* User VA - Base User VA = User Offset assuming not tiler 2D */
+ /* For Tiler2D offset is corrected later */
+ long uoff = uva - buva;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "CORE=%u BUVA=%p UVA=%p Uoff=%ld [0x%016lx] Hdl=%p\n",
+ core, (void *)buva, (void *)uva, uoff, (ulong) uoff,
+ reserved);
+
+ if (uoff < 0) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Offsets calculation for BUVA=%p from UVA=%p is a "
+ "negative number. Bad parameters!\n",
+ (void *)buva, (void *)uva);
+ rpa = 0;
+ goto to_end;
+ }
+
+ if (reserved) {
+ struct ion_handle *handle;
+ ion_phys_addr_t paddr;
+ size_t len;
+
+ /* is it an ion handle? */
+ handle = (struct ion_handle *)reserved;
+ if (!ion_phys(rpc->ion_client, handle, &paddr, &len)) {
+ lpa = (phys_addr_t) paddr;
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "Handle %p is an ION Handle to ARM PA %p "
+ "(Uoff=%ld) (len=%zu)\n", reserved, (void *)lpa,
+ uoff, len);
+ if ((long)len < uoff) {
+ /* if the offset is larger than the length,
+ * there is a potential security issue.
+ */
+ pr_err("Offset %ld too large, must be < %zu\n",
+ uoff, len);
+ rpa = 0;
+ goto to_end;
+ }
+ /* recalculate for 2d strides */
+ uoff = omaprpc_recalc_off(lpa, uoff);
+ lpa += uoff;
+ goto to_va;
+ } else {
+ /* is it an pvr buffer wrapping an ion handle? */
+
+ /*
+ * TODO: need to support 2 ion handles
+ * per 1 pvr handle (NV12 case)
+ */
+ struct ion_buffer *ion_buffer = NULL;
+ int num_handles = 1;
+ handle = NULL;
+ if (omap_ion_share_fd_to_buffers((int)reserved,
+ &ion_buffer,
+ &num_handles) < 0) {
+ goto to_va;
+ }
+ if (ion_buffer) {
+ handle = ion_import(rpc->ion_client,
+ ion_buffer);
+ }
+ if (handle && !ion_phys(rpc->ion_client, handle,
+ &paddr, &len)) {
+ lpa = (phys_addr_t) paddr;
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev,
+ "FD %d is an PVR Handle to ARM PA %p "
+ "(Uoff=%ld)\n", (int)reserved,
+ (void *)lpa, uoff);
+ if ((long)len < uoff) {
+ /* if the offset is larger than the
+ * length there is a security issue.
+ */
+ pr_err(
+ "Offset %ld too large, must be < %zu\n",
+ uoff, len);
+ rpa = 0;
+ goto to_end;
+ }
+ uoff = omaprpc_recalc_off(lpa, uoff);
+ lpa += uoff;
+ goto to_va;
+ }
+ /*
+ * TODO: need to do some buffer tracking and call
+ * ion_free when it is no longer being used. this
+ * will make sure the buffer is not freed while
+ * we are still using it
+ */
+ if (handle)
+ ion_free(rpc->ion_client, handle);
+ }
+ }
+
+to_va:
+ /* convert the local physical address to remote physical address */
+ rpa = rpmsg_local_to_remote_pa(rpc, lpa);
+to_end:
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "ARM VA %p == ARM PA %p => REMOTE[%u] PA %p (RESV %p)\n",
+ (void *)uva, (void *)lpa, core, (void *)rpa, reserved);
+ return rpa;
+}
+
+int omaprpc_xlate_buffers(struct omaprpc_instance_t *rpc,
+ struct omaprpc_call_function_t *function,
+ int direction)
+{
+ int idx = 0, start = 0, inc = 1, limit = 0, ret = 0;
+ uint32_t ptr_idx = 0, offset = 0, size = 0;
+ /* not all the parameters are pointers so this may be sparse */
+ uint8_t *base_ptrs[OMAPRPC_MAX_PARAMETERS];
+
+ if (function->num_translations == 0)
+ return 0;
+
+ limit = function->num_translations;
+ memset(base_ptrs, 0, sizeof(base_ptrs));
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "Operating on %d pointers\n", function->num_translations);
+ /*
+ * we may have a failure during translation, in which case we
+ * need to unwind the whole operation from here
+ */
+restart:
+ for (idx = start; idx != limit; idx += inc) {
+ /* conveinence variables */
+ ptr_idx = function->translations[idx].index;
+ offset = function->translations[idx].offset;
+
+ /* if the pointer index for this translation is invalid */
+ if (ptr_idx >= OMAPRPC_MAX_PARAMETERS) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Invalid parameter pointer index %u\n",
+ ptr_idx);
+ goto unwind;
+ } else if (function->params[ptr_idx].type !=
+ OMAPRPC_PARAM_TYPE_PTR) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Parameter index %u is not a pointer (type %u)"
+ "\n", ptr_idx,
+ function->params[ptr_idx].type);
+ goto unwind;
+ }
+
+ size = function->params[ptr_idx].size;
+
+ if (offset >= (size - sizeof(virt_addr_t))) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Offset is larger than data area! "
+ "(offset=%u size=%u)\n", offset, size);
+ goto unwind;
+ }
+
+ if (function->params[ptr_idx].data == 0) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Supplied user pointer is NULL!\n");
+ goto unwind;
+ }
+
+ /* if the KVA pointer has not been mapped */
+ if (base_ptrs[ptr_idx] == NULL) {
+ /*
+ * map the UVA pointer to KVA space, the offset could
+ * potentially be modified due to the mapping
+ */
+ base_ptrs[ptr_idx] = omaprpc_map_parameter(rpc,
+ &function->
+ params
+ [ptr_idx]);
+ }
+
+ /* if the KVA pointer is not NULL */
+ if (base_ptrs[ptr_idx] != NULL) {
+ if (direction == OMAPRPC_UVA_TO_RPA) {
+ /*
+ * get the kernel virtual pointer to
+ * the pointer to translate
+ */
+ virt_addr_t kva = (virt_addr_t)
+ &((base_ptrs[ptr_idx])[offset]);
+ virt_addr_t uva = 0;
+ virt_addr_t buva = (virt_addr_t)
+ function->translations[idx].base;
+ phys_addr_t rpa = 0;
+ void *reserved = (void *)
+ function->translations[idx].reserved;
+
+ /*
+ * make sure we won't cause
+ * an unalign mem access
+ */
+ if ((kva & 0x3) > 0) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "ERROR: KVA %p is unaligned!\n",
+ (void *)kva);
+ return -EADDRNOTAVAIL;
+ }
+ /* load the user's VA */
+ uva = *(virt_addr_t *) kva;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev,
+ "Replacing UVA %p at KVA %p PTRIDX:%u "
+ "OFFSET:%u IDX:%d\n",
+ (void *)uva, (void *)kva, ptr_idx,
+ offset, idx);
+
+ /* calc the new RPA (remote physical address) */
+ rpa = omaprpc_buffer_lookup(rpc, rpc->core,
+ uva, buva,
+ reserved);
+ /* save the old value */
+ function->translations[idx].reserved = uva;
+ /* replace with new RPA */
+ *(phys_addr_t *) kva = rpa;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev,
+ "Replaced UVA %p with RPA %p at KVA %p\n",
+ (void *)uva, (void *)rpa,
+ (void *)kva);
+
+ if (rpa == 0) {
+ /* need to unwind all operations.. */
+ direction = OMAPRPC_RPA_TO_UVA;
+ start = idx - 1;
+ inc = -1;
+ limit = -1;
+ ret = -ENODATA;
+ goto restart;
+ }
+ } else if (direction == OMAPRPC_RPA_TO_UVA) {
+ /* address of the pointer in memory */
+ virt_addr_t kva = (virt_addr_t) &
+ ((base_ptrs[ptr_idx])[offset]);
+ virt_addr_t uva = 0;
+ phys_addr_t rpa = 0;
+ /*
+ * make sure we won't cause
+ * an unalign mem access
+ */
+ if ((kva & 0x3) > 0)
+ return -EADDRNOTAVAIL;
+ /* get what was there for debugging */
+ rpa = *(phys_addr_t *) kva;
+ /* convienence value of uva */
+ uva = (virt_addr_t)
+ function->translations[idx].reserved;
+ /*
+ * replace the translated value
+ * with the remember version
+ */
+ *(virt_addr_t *) kva = uva;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev,
+ "Replaced RPA %p with UVA %p at KVA %p\n",
+ (void *)rpa, (void *)uva,
+ (void *)kva);
+
+ if (uva == 0) {
+ /* need to unwind all operations.. */
+ direction = OMAPRPC_RPA_TO_UVA;
+ start = idx - 1;
+ inc = -1;
+ limit = -1;
+ ret = -ENODATA;
+ goto restart;
+ }
+ }
+ } else {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Failed to map UVA to KVA to do translation!"
+ "\n");
+ /*
+ * we can arrive here from multiple points,
+ * but the action is the same from everywhere
+ */
+unwind:
+ if (direction == OMAPRPC_UVA_TO_RPA) {
+ /*
+ * we've encountered an error which
+ * needs to unwind all the operations
+ */
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Unwinding UVA to RPA translations!\n");
+ direction = OMAPRPC_RPA_TO_UVA;
+ start = idx - 1;
+ inc = -1;
+ limit = -1;
+ ret = -ENOBUFS;
+ goto restart;
+ } else if (direction == OMAPRPC_RPA_TO_UVA) {
+ /*
+ *there was a problem restoring the pointer,
+ * there's nothing to do but to continue
+ * processing
+ */
+ continue;
+ }
+ }
+ }
+ /* unmap all the pointers that were mapped and not freed yet */
+ for (idx = 0; idx < OMAPRPC_MAX_PARAMETERS; idx++) {
+ if (base_ptrs[idx]) {
+ omaprpc_unmap_parameter(rpc,
+ &function->params[idx],
+ base_ptrs[idx], 0);
+ base_ptrs[idx] = NULL;
+ }
+ }
+ return ret;
+}
diff --git a/drivers/rpmsg/omaprpc/omap_rpc_rproc.c b/drivers/rpmsg/omaprpc/omap_rpc_rproc.c
new file mode 100644
index 0000000..8edf338
--- /dev/null
+++ b/drivers/rpmsg/omaprpc/omap_rpc_rproc.c
@@ -0,0 +1,93 @@
+/*
+ * OMAP Remote Procedure Call Driver.
+ *
+ * Copyright(c) 2012 Texas Instruments. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "omap_rpc_internal.h"
+
+#if defined(OMAPRPC_USE_RPROC_LOOKUP)
+
+phys_addr_t rpmsg_local_to_remote_pa(struct omaprpc_instance_t *rpc,
+ phys_addr_t pa)
+{
+ int ret;
+ struct rproc *rproc;
+ u64 da;
+ phys_addr_t rpa;
+
+ if (mutex_lock_interruptible(&rpc->rpcserv->lock))
+ return 0;
+
+ rproc = rpmsg_get_rproc_handle(rpc->rpcserv->rpdev);
+ if (!rproc)
+ pr_err("error getting rproc id %p\n",
+ rpc->rpcserv->rpdev);
+ else {
+ ret = rproc_pa_to_da(rproc, pa, &da);
+ if (ret) {
+ pr_err("error from rproc_pa_to_da %d\n", ret);
+ da = 0;
+ }
+ }
+ /*Revisit if remote address size increases */
+ rpa = (phys_addr_t) da;
+
+ mutex_unlock(&rpc->rpcserv->lock);
+ return rpa;
+
+}
+
+#else
+
+struct remote_mmu_region_t {
+ phys_addr_t tiler_start;
+ phys_addr_t tiler_end;
+ phys_addr_t ion_1d_start;
+ phys_addr_t ion_1d_end;
+ phys_addr_t ion_1d_va;
+};
+
+static struct remote_mmu_region_t regions[OMAPRPC_CORE_REMOTE_MAX] = {
+ /* Tesla */
+ {0x60000000, 0x80000000, 0xBA300000, 0xBFD00000, 0x88000000},
+ /* SIMCOP */
+ {0x60000000, 0x80000000, 0xBA300000, 0xBFD00000, 0x88000000},
+ /* MCU0 */
+ {0x60000000, 0x80000000, 0xBA300000, 0xBFD00000, 0x88000000},
+ /* MCU1 */
+ {0x60000000, 0x80000000, 0xBA300000, 0xBFD00000, 0x88000000},
+ /* EVE */
+ {0x60000000, 0x80000000, 0xBA300000, 0xBFD00000, 0x88000000},
+};
+
+static u32 numCores = sizeof(regions) / sizeof(regions[0]);
+
+phys_addr_t rpmsg_local_to_remote_pa(uint32_t core, phys_addr_t pa)
+{
+ if (core < numCores) {
+ if (regions[core].tiler_start <= pa &&
+ pa < regions[core].tiler_end)
+ return pa;
+ else if (regions[core].ion_1d_start <= pa &&
+ pa < regions[core].ion_1d_end)
+ return (pa - regions[core].ion_1d_start) +
+ regions[core].ion_1d_va;
+ }
+ return 0;
+}
+
+#endif
+
diff --git a/drivers/rpmsg/omaprpc/omap_rpc_tiler.c b/drivers/rpmsg/omaprpc/omap_rpc_tiler.c
new file mode 100644
index 0000000..d36fe1e
--- /dev/null
+++ b/drivers/rpmsg/omaprpc/omap_rpc_tiler.c
@@ -0,0 +1,59 @@
+/*
+ * OMAP Remote Procedure Call Driver.
+ *
+ * Copyright(c) 2012 Texas Instruments. All rights reserved.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "omap_rpc_internal.h"
+
+/**
+ * tiler_stride_from_region() - Returns the stride value as seen by remote cores
+ * based on the local address given to the function. This stride value is valid
+ * for non-local cores only.
+ * @localphys: The local physical address.
+ **/
+static inline long tiler_stride_from_region(phys_addr_t localphys)
+{
+ /*
+ * The physical address range decoding of local addresses is as follows:
+ *
+ * 0x60000000 - 0x67FFFFFF : 8-bit region
+ * 0x68000000 - 0x6FFFFFFF : 16-bit region
+ * 0x70000000 - 0x77FFFFFF : 32-bit region
+ * 0x77000000 - 0x7FFFFFFF : Page mode region
+ */
+ switch (localphys & 0xf8000000) { /* Mask out the lower bits */
+ case 0x60000000: /* 8-bit */
+ return 0x4000; /* 16 KB of stride */
+ case 0x68000000: /* 16-bit */
+ case 0x70000000: /* 32-bit */
+ return 0x8000; /* 32 KB of stride */
+ default:
+ return 0;
+ }
+}
+
+/**
+ * omaprpc_recalc_off() - Recalculated the unsigned offset in a buffer due to
+ * it's location in the TILER.
+ * @lpa: local physical address
+ * @uoff: unsigned offset
+ */
+long omaprpc_recalc_off(phys_addr_t lpa, long uoff)
+{
+ long stride = tiler_stride_from_region(lpa);
+ return (stride != 0) ? (stride * (uoff / PAGE_SIZE)) +
+ (uoff & (PAGE_SIZE - 1)) : uoff;
+}