summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorErik Rainey <erik.rainey@ti.com>2012-08-16 12:06:46 +0200
committerAndy Green <andy.green@linaro.org>2012-09-07 13:05:58 +0800
commit4f695ebd7c16589c4bb1a6d81b61956c44205741 (patch)
treed0953afe64abb11053119eaf09c36b1e6c9753c9 /drivers
parent885fc9773ccca299813c6db809b958c3d28ea6ca (diff)
omaprpc: Unified Patch to add OMAP Remote Procedure Call Driver
Patch History (squashed), contributors initials follow history. omaprpc: Adding OMAP Remote Procedure Call Driver to staging area of kernel. [ER] omaprpc: Adding defines for CONFIG_DRM_OMAP and isolating Android parts. [ER] omaprpc: Latest commit, compiles and produces a working kernel, no ducati/tesla to test with. [ER] omaprpc: Added dependency on DMA_SHARED_BUFFER and changed some dev_info prints. [ER] omaprpc: fixing omaprpc makefile to build omap_rpc.c. [GA] omaprpc: commenting some debug in omap_rpc_dmabuf.c that doesn't compile. [GA] omaprpc: enable OMAPRPC_INFO debug messages. [GA] omaprpc: changing char dev creation procedure. [GA] create_device() is now called in the probe() function. omaprpc_device_create() and omaprpc_driver_cb() are commented, because not compiling. omaprpc: fixing char dev name [GA] omaprpc: renaming iMX to SIMCOP [ER] omaprpc: Added dynamic dev node naming back, updated header files. kmaps still do not work correctly [AA][ER] omaprpc: Added safety code to make sure dma_bufs behave well. Adjusted ranges for cpu_access [ER] omaprpc: Updates to copyright and MODULE_DEVICE_TABLE. Also removed unneeded header [ER] omaprpc: Fixes from Android [ER][AA] omaprpc: Unified OMAP Remote Procedure Call Driver [ER] omaprpc: Fixes to coding style [ER] omaprpc: Added bootargs debug flag [ER] omaprpc: Changed some formatting for readibility [ER] omaprpc: Fix to forcefully unpin all dmabufs after a transaction [ER] Signed-off By: Erik Rainey <erik.rainey@ti.com> Signed-off By: Guillaume Aubertin <g-aubertin@ti.com> Signed-off By: Alberto Aguirre <a-aguirre@ti.com> Signed-off-by: Xavier Boudet <x-boudet@ti.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/staging/Kconfig2
-rw-r--r--drivers/staging/Makefile3
-rw-r--r--drivers/staging/omaprpc/Kconfig15
-rw-r--r--drivers/staging/omaprpc/Makefile17
-rw-r--r--drivers/staging/omaprpc/omap_rpc.c1324
-rw-r--r--drivers/staging/omaprpc/omap_rpc.h287
-rw-r--r--drivers/staging/omaprpc/omap_rpc_dmabuf.c475
-rw-r--r--drivers/staging/omaprpc/omap_rpc_internal.h214
-rw-r--r--drivers/staging/omaprpc/omap_rpc_ion.c340
-rw-r--r--drivers/staging/omaprpc/omap_rpc_rproc.c103
-rw-r--r--drivers/staging/omaprpc/omap_rpc_tiler.c62
11 files changed, 2841 insertions, 1 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 4ad02c9905ad..59a39a77dc53 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -124,6 +124,8 @@ source "drivers/staging/media/Kconfig"
source "drivers/staging/omapdrm/Kconfig"
+source "drivers/staging/omaprpc/Kconfig"
+
source "drivers/staging/android/Kconfig"
source "drivers/staging/telephony/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index a3bc940f1366..df5afd824cab 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -53,10 +53,11 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/
obj-$(CONFIG_INTEL_MEI) += mei/
obj-$(CONFIG_MFD_NVEC) += nvec/
obj-$(CONFIG_DRM_OMAP) += omapdrm/
+obj-$(CONFIG_RPC_OMAP) += omaprpc/
obj-$(CONFIG_ANDROID) += android/
obj-$(CONFIG_PHONE) += telephony/
obj-$(CONFIG_RAMSTER) += ramster/
obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/
obj-$(CONFIG_OMAP_THERMAL) += thermal_framework/
obj-$(CONFIG_DRM_OMAP_DCE) += omapdce/
-obj-$(CONFIG_OMAP_RPMSG_RDAEMON) += rdaemon/ \ No newline at end of file
+obj-$(CONFIG_OMAP_RPMSG_RDAEMON) += rdaemon/
diff --git a/drivers/staging/omaprpc/Kconfig b/drivers/staging/omaprpc/Kconfig
new file mode 100644
index 000000000000..93b3beb76b58
--- /dev/null
+++ b/drivers/staging/omaprpc/Kconfig
@@ -0,0 +1,15 @@
+
+config RPC_OMAP
+ tristate "OMAP Remote Procedure Call driver"
+ default m
+ depends on RPMSG
+ depends on (TI_TILER && ION_OMAP) || (DMA_SHARED_BUFFER && DRM_OMAP)
+ depends on REMOTEPROC || REMOTE_PROC
+ depends on OMAP_REMOTEPROC || OMAP_REMOTE_PROC
+ ---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.
+
diff --git a/drivers/staging/omaprpc/Makefile b/drivers/staging/omaprpc/Makefile
new file mode 100644
index 000000000000..a02ed9ef20df
--- /dev/null
+++ b/drivers/staging/omaprpc/Makefile
@@ -0,0 +1,17 @@
+
+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
+endif
+
+ifeq ($(CONFIG_DMA_SHARED_BUFFER),y)
+omaprpc-y += omap_rpc_dmabuf.o
+endif
+
+
+
+
diff --git a/drivers/staging/omaprpc/omap_rpc.c b/drivers/staging/omaprpc/omap_rpc.c
new file mode 100644
index 000000000000..fb44d6dd88d2
--- /dev/null
+++ b/drivers/staging/omaprpc/omap_rpc.c
@@ -0,0 +1,1324 @@
+/*
+ * OMAP Remote Procedure Call Driver.
+ *
+ * Copyright(c) 2012 Texas Instruments. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Texas Instruments nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#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 = 0;
+EXPORT_SYMBOL(omaprpc_debug);
+MODULE_PARM_DESC(debug, "Used to enable debug message");
+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 ret; /* return the length of the data written to us */
+}
+
+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;
+
+ 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) {
+ dev_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:
+ {
+ int 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;
+ }
+ }
+}
+
+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! "
+ "debug=%d\n",
+ ret, 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/staging/omaprpc/omap_rpc.h b/drivers/staging/omaprpc/omap_rpc.h
new file mode 100644
index 000000000000..5a5000ee6c1a
--- /dev/null
+++ b/drivers/staging/omaprpc/omap_rpc.h
@@ -0,0 +1,287 @@
+/*
+ * OMAP Remote Procedure Call Driver.
+ *
+ * Copyright(c) 2011 Texas Instruments. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Texas Instruments nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OMAP_RPC_H_
+#define _OMAP_RPC_H_
+
+#include <linux/ioctl.h>
+
+#if defined(CONFIG_ION_OMAP)
+#include <linux/ion.h>
+#endif
+
+#define OMAPRPC_IOC_MAGIC 'O'
+
+#define OMAPRPC_IOC_CREATE _IOW(OMAPRPC_IOC_MAGIC, 1, char *)
+#define OMAPRPC_IOC_DESTROY _IO(OMAPRPC_IOC_MAGIC, 2)
+#define OMAPRPC_IOC_IONREGISTER _IOWR(OMAPRPC_IOC_MAGIC, 3, \
+ struct ion_fd_data)
+#define OMAPRPC_IOC_IONUNREGISTER _IOWR(OMAPRPC_IOC_MAGIC, 4, \
+ struct ion_fd_data)
+
+#define OMAPRPC_IOC_MAXNR (4)
+
+struct omaprpc_create_instance_t {
+ char name[48];
+};
+
+struct omaprpc_channel_info_t {
+ char name[64];
+};
+
+enum omaprpc_info_type_e {
+ /** The number of functions in the service instance */
+ OMAPRPC_INFO_NUMFUNCS,
+ /** The symbol name of the function */
+ OMAPRPC_INFO_FUNC_NAME,
+ /** The number of times a function has been called */
+ OMAPRPC_INFO_NUM_CALLS,
+ /** The performance information releated to the function */
+ OMAPRPC_INFO_FUNC_PERF,
+
+ /** @hidden used to define the maximum info type */
+ OMAPRPC_INFO_MAX
+};
+
+struct omaprpc_query_instance_t {
+ uint32_t info_type; /**< @see omaprpc_info_type_e */
+ uint32_t func_index; /**< The index to querty */
+};
+
+struct omaprpc_func_perf_t {
+ uint32_t clocks_per_sec;
+ uint32_t clock_cycles;
+};
+
+/** These core are specific to OMAP processors */
+enum omaprpc_core_e {
+ OMAPRPC_CORE_DSP = 0, /**< DSP Co-processor */
+ OMAPRPC_CORE_SIMCOP, /**< Video/Imaging Co-processor */
+ OMAPRPC_CORE_MCU0, /**< Cortex M3/M4 [0] */
+ OMAPRPC_CORE_MCU1, /**< Cortex M3/M4 [1] */
+ OMAPRPC_CORE_EVE, /**< Imaging Accelerator */
+ OMAPRPC_CORE_REMOTE_MAX
+};
+
+struct omaprpc_instance_info_t {
+ uint32_t info_type;
+ uint32_t func_index;
+ union info {
+ uint32_t num_funcs;
+ uint32_t num_calls;
+ uint32_t core_index; /**< @see omaprpc_core_e */
+ char func_name[64];
+ struct omaprpc_func_perf_t perf;
+ } info;
+};
+
+enum omaprpc_cache_ops_e {
+ OMAPRPC_CACHEOP_NONE = 0,
+ OMAPRPC_CACHEOP_FLUSH,
+ OMAPRPC_CACHEOP_INVALIDATE,
+
+ OMAPRPC_CACHEOP_MAX,
+};
+
+struct omaprpc_param_translation_t {
+ /** The parameter index which indicates which is the base pointer */
+ uint32_t index;
+ /** The offset from the base address to the pointer to translate */
+ ptrdiff_t offset;
+ /** The base user virtual address of the pointer to translate
+ (used to calc translated pointer offset). */
+ size_t base;
+ /** The enumeration of desired cache operations for efficiency */
+ uint32_t cacheOps;
+ /** Reserved field */
+ size_t reserved;
+};
+
+enum omaprpc_param_e {
+ OMAPRPC_PARAM_TYPE_UNKNOWN = 0,
+ /** An atomic data type, 1 byte to architecture limit sized bytes */
+ OMAPRPC_PARAM_TYPE_ATOMIC,
+ /** A pointer to shared memory. The reserved field must contain the
+ handle to the memory */
+ OMAPRPC_PARAM_TYPE_PTR,
+ /** \hidden (Unsupported) A structure type. Will be architecure width
+ aligned in memory. */
+ OMAPRPC_PARAM_TYPE_STRUCT,
+};
+
+struct omaprpc_param_t {
+ uint32_t type; /**< @see omaprpc_param_e */
+ size_t size; /**< The size of the data */
+ size_t data; /**< Either the pointer to the data or the data
+ itself, @see .type */
+ size_t base; /**< If a pointer is in data, this is the base
+ pointer (if data has an offset from base). */
+ size_t reserved; /**< Shared Memory Handle
+ (used only with pointers) */
+};
+
+#define OMAPRPC_MAX_PARAMETERS (10)
+
+struct omaprpc_call_function_t {
+ /** The function to call */
+ uint32_t func_index;
+ /** The number of parameters in the array. */
+ uint32_t num_params;
+ /** The array of parameters */
+ struct omaprpc_param_t params[OMAPRPC_MAX_PARAMETERS];
+ /** The number of translations needed in the offsets array */
+ uint32_t num_translations;
+ /** An indeterminate lenght array of offsets within payload_data to
+ pointers which need translation */
+ struct omaprpc_param_translation_t translations[0];
+};
+
+#define OMAPRPC_MAX_TRANSLATIONS (1024)
+
+struct omaprpc_function_return_t {
+ uint32_t func_index;
+ uint32_t status;
+};
+
+#ifdef __KERNEL__
+
+/** The applicable types of messages that the HOST may send the SERVICE.
+ * (@see omx_msg_types must duplicate these for now since they went and shoved
+ * it so far down RCM that it's impossible to use it without this)
+ */
+enum omaprpc_msg_type_e {
+ /** Ask the ServiceMgr to create a new instance of the service.
+ * No secondary data is needed. */
+ OMAPRPC_MSG_CREATE_INSTANCE = 0,
+ /** The return message from OMAPRPC_CREATE_INSTANCE,
+ * contains the new endpoint address in the omaprpc_instance_handle_t */
+ OMAPRPC_MSG_INSTANCE_CREATED = 1,
+ /** Ask the Service Instance to send information about the Service */
+ OMAPRPC_MSG_QUERY_INSTANCE = 2,
+ /** The return message from OMAPRPC_QUERY_INSTANCE,
+ * which contains the information about the instance */
+ OMAPRPC_MSG_INSTANCE_INFO = 3,
+ /** Ask the Service Mgr to destroy an instance */
+ OMAPRPC_MSG_DESTROY_INSTANCE = 4,
+ /** Ask the Service Instance to call a particular function */
+ OMAPRPC_MSG_CALL_FUNCTION = 5,
+ /** The return message from OMAPRPC_DESTROY_INSTANCE.
+ * contains the old endpoint address in the omaprpc_instance_handle_t */
+ OMAPRPC_MSG_INSTANCE_DESTROYED = 6,
+ /** Returned from either the ServiceMgr or Service Instance
+ * when an error occurs */
+ OMAPRPC_MSG_ERROR = 7,
+ /** The return values from a function call */
+ OMAPRPC_MSG_FUNCTION_RETURN = 8,
+ /** Ask Service for channel information*/
+ OMAPRPC_MSG_QUERY_CHAN_INFO = 9,
+ /** The return message from OMAPRPC_MSG_QUERY_CHAN_INFO*/
+ OMAPRPC_MSG_CHAN_INFO = 10,
+
+ /** \hidden used to define the max msg enum, not an actual message */
+ OMAPRPC_MSG_MAX
+};
+
+enum omaprpc_state {
+ /** No errors, just not initialized */
+ OMAPRPC_STATE_DISCONNECTED,
+ /** No errors, initialized remote DVP KGM */
+ OMAPRPC_STATE_CONNECTED,
+ /** Some error has been detected. Disconnected. */
+ OMAPRPC_STATE_FAULT,
+
+ /* \hidden Last item in enum */
+ OMAPRPC_STATE_MAX
+};
+
+/** \brief The generic OMAPRPC message header.
+ * (actually a copy of omx_msg_hdr which is a copy of an RCM header) */
+struct omaprpc_msg_header_t {
+ uint32_t msg_type; /**< @see omaprpc_msg_type_e */
+ uint32_t msg_flags; /**< Unused */
+ uint32_t msg_len; /**< The length of the message data in bytes */
+ uint8_t msg_data[0];
+} __packed;
+
+struct omaprpc_instance_handle_t {
+ uint32_t endpoint_address;
+ uint32_t status;
+} __packed;
+
+struct omaprpc_error_t {
+ uint32_t endpoint_address;
+ uint32_t status;
+} __packed;
+
+struct omaprpc_parameter_t {
+ size_t size;
+ size_t data;
+} __packed;
+
+#define OMAPRPC_NUM_PARAMETERS(size) \
+ (size/sizeof(struct omaprpc_parameter_t))
+
+#define OMAPRPC_PAYLOAD(ptr, type) \
+ ((struct type *)&(ptr)[sizeof(struct omaprpc_msg_header_t)])
+
+enum _omaprpc_translation_direction_e {
+ OMAPRPC_UVA_TO_RPA,
+ OMAPRPC_RPA_TO_UVA,
+};
+
+#endif /* __KERNEL__ */
+
+#define OMAPRPC_DESC_EXEC_SYNC (0x0100)
+#define OMAPRPC_DESC_EXEC_ASYNC (0x0200)
+#define OMAPRPC_DESC_SYM_ADD (0x0300)
+#define OMAPRPC_DESC_SYM_IDX (0x0400)
+#define OMAPRPC_DESC_CMD (0x0500)
+#define OMAPRPC_DESC_TYPE_MASK (0x0F00)
+#define OMAPRPC_JOBID_DISCRETE (0)
+#define OMAPRPC_POOLID_DEFAULT (0x8000)
+
+#define OMAPRPC_SET_FXN_IDX(idx) (idx | 0x80000000)
+#define OMAPRPC_FXN_MASK(idx) (idx & 0x7FFFFFFF)
+
+/** This is actually a frankensteined structure of RCM */
+struct omaprpc_packet_t {
+ uint16_t desc; /**< @see RcmClient_Packet.desc */
+ uint16_t msg_id; /**< @see RcmClient_Packet.msgId */
+ uint16_t pool_id; /**< @see RcmClient_Message.poolId */
+ uint16_t job_id; /**< @see RcmClient_Message.jobId */
+ uint32_t fxn_idx; /**< @see RcmClient_Message.fxnIdx */
+ int32_t result; /**< @see RcmClient_Message.result */
+ uint32_t data_size; /**< @see RcmClient_Message.data_size */
+ uint8_t data[0]; /**< @see RcmClient_Message.data pointer */
+} __packed;
+
+#endif /* _OMAP_RPC_H_ */
diff --git a/drivers/staging/omaprpc/omap_rpc_dmabuf.c b/drivers/staging/omaprpc/omap_rpc_dmabuf.c
new file mode 100644
index 000000000000..05fbb39871b6
--- /dev/null
+++ b/drivers/staging/omaprpc/omap_rpc_dmabuf.c
@@ -0,0 +1,475 @@
+/*
+ * OMAP Remote Procedure Call Driver.
+ *
+ * Copyright(c) 2012 Texas Instruments. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Texas Instruments nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#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 struct dma_info_t *omaprpc_dma_sub(struct omaprpc_instance_t *rpc,
+ int fd)
+{
+ 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,
+ "Looking for FD %u, found FD %u\n", fd, pos->fd);
+ if (pos->fd == fd) {
+ list_del((struct list_head *)pos);
+ break;
+ } else
+ pos = NULL;
+ }
+ mutex_unlock(&rpc->lock);
+ return pos;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+void omaprpc_unpin_buffer(struct omaprpc_instance_t *rpc, void *reserved)
+{
+ struct dma_info_t *dma = omaprpc_dma_sub(rpc, (int)reserved);
+ if (dma == NULL)
+ return;
+ dma_buf_unmap_attachment(dma->attach, dma->sgt, DMA_BIDIRECTIONAL);
+ dma_buf_detach(dma->dbuf, dma->attach);
+ dma_buf_put(dma->dbuf);
+ kfree(dma);
+}
+
+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/staging/omaprpc/omap_rpc_internal.h b/drivers/staging/omaprpc/omap_rpc_internal.h
new file mode 100644
index 000000000000..eef34cab22ff
--- /dev/null
+++ b/drivers/staging/omaprpc/omap_rpc_internal.h
@@ -0,0 +1,214 @@
+/*
+ * OMAP Remote Procedure Call Driver.
+ *
+ * Copyright(c) 2012 Texas Instruments. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Texas Instruments nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#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 requireds 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_DMA_SHARED_BUFFER) \
+|| defined(CONFIG_DMA_SHARED_BUFFER_MODULE)
+#define OMAPRPC_USE_DMABUF
+#undef OMAPRPC_USE_RPROC_LOOKUP /* genernic linux does not support yet. */
+#else
+#undef OMAPRPC_USE_DMABUF
+#endif
+
+#if defined(CONFIG_ION_OMAP) || defined(CONFIG_ION_OMAP_MODULE)
+#define OMAPRPC_USE_ION
+#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
+#else
+#undef OMAPRPC_USE_ION
+#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 {
+ 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 {
+ struct list_head list;
+ struct omaprpc_call_function_t *function;
+ u16 msgId;
+};
+
+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 {
+ 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/staging/omaprpc/omap_rpc_ion.c b/drivers/staging/omaprpc/omap_rpc_ion.c
new file mode 100644
index 000000000000..4d74e39c2d9d
--- /dev/null
+++ b/drivers/staging/omaprpc/omap_rpc_ion.c
@@ -0,0 +1,340 @@
+/*
+ * OMAP Remote Procedure Call Driver.
+ *
+ * Copyright(c) 2012 Texas Instruments. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Texas Instruments nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "omap_rpc_internal.h"
+
+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;
+
+}
+
+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 defined(OMAPRPC_USE_ION)
+ if (reserved) {
+ struct ion_handle *handle;
+ ion_phys_addr_t paddr;
+ size_t unused;
+
+ /* is it an ion handle? */
+ handle = (struct ion_handle *)reserved;
+ if (!ion_phys(rpc->ion_client, handle, &paddr, &unused)) {
+ 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)\n", reserved, (void *)lpa, uoff);
+ uoff = omaprpc_recalc_off(lpa, uoff);
+ lpa += uoff;
+ goto to_va;
+ } else {
+ /* is it an pvr buffer wrapping an ion handle? */
+ struct ion_client *pvr_ion_client;
+ /* @TODO need to support 2 ion handles per 1 pvr handle
+ (NV12 case) */
+ int num_handles = 1;
+ handle = NULL;
+ if (omap_ion_fd_to_handles((int)reserved,
+ &pvr_ion_client, &handle, &num_handles) < 0) {
+ goto to_va;
+ }
+ if (handle && !ion_phys(pvr_ion_client, handle, &paddr,
+ &unused)) {
+ 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);
+ uoff = omaprpc_recalc_off(lpa, uoff);
+ lpa += uoff;
+ goto to_va;
+ }
+ }
+ }
+#endif
+
+ /* Ask the TILER to convert from virtual to physical */
+ lpa = (phys_addr_t)tiler_virt2phys(uva);
+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;
+ /* @NOTE 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/staging/omaprpc/omap_rpc_rproc.c b/drivers/staging/omaprpc/omap_rpc_rproc.c
new file mode 100644
index 000000000000..33fd0aaf4543
--- /dev/null
+++ b/drivers/staging/omaprpc/omap_rpc_rproc.c
@@ -0,0 +1,103 @@
+/*
+ * OMAP Remote Procedure Call Driver.
+ *
+ * Copyright(c) 2012 Texas Instruments. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Texas Instruments nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#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);
+ 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/staging/omaprpc/omap_rpc_tiler.c b/drivers/staging/omaprpc/omap_rpc_tiler.c
new file mode 100644
index 000000000000..8528bed118b9
--- /dev/null
+++ b/drivers/staging/omaprpc/omap_rpc_tiler.c
@@ -0,0 +1,62 @@
+/*
+ * OMAP Remote Procedure Call Driver.
+ *
+ * Copyright(c) 2012 Texas Instruments. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Texas Instruments nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "omap_rpc_internal.h"
+
+static inline long tiler_stride(phys_addr_t lpa)
+{
+ /*
+ * The access mode decoding is as follows:
+ *
+ * 0x60000000 - 0x67FFFFFF : 8-bit
+ * 0x68000000 - 0x6FFFFFFF : 16-bit
+ * 0x70000000 - 0x77FFFFFF : 32-bit
+ * 0x77000000 - 0x7FFFFFFF : Page mode
+ */
+ switch (lpa & 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;
+ }
+}
+
+long omaprpc_recalc_off(phys_addr_t lpa, long uoff)
+{
+ long stride = tiler_stride(lpa);
+ return (stride != 0) ? (stride*(uoff/PAGE_SIZE)) +
+ (uoff & (PAGE_SIZE-1)) : uoff;
+}
+