diff options
author | Erik Rainey <erik.rainey@ti.com> | 2012-05-01 13:46:13 -0500 |
---|---|---|
committer | Dmytro Kedrovskyi <x0169235@ti.com> | 2012-05-16 15:28:36 +0300 |
commit | b80985f5d1b8237f6ca8a346f2e33ac83ee663e5 (patch) | |
tree | 6828e0c1c398ec54ffffe631b068f2e39ff03463 | |
parent | e63d79b92e20054c5be1431815204262fe41ddc9 (diff) | |
download | kernel_samsung_espresso10-b80985f5d1b8237f6ca8a346f2e33ac83ee663e5.zip kernel_samsung_espresso10-b80985f5d1b8237f6ca8a346f2e33ac83ee663e5.tar.gz kernel_samsung_espresso10-b80985f5d1b8237f6ca8a346f2e33ac83ee663e5.tar.bz2 |
Add a generic OMAP Remote Procedure Call Driver
Change-Id: I7c54eb41f62c104c7a1d0f525e1fd85450441cd3
Signed-off-by: Erik Rainey <erik.rainey@ti.com>
Signed-off-by: Alberto Aguirre <a-aguirre@ti.com>
-rw-r--r-- | drivers/staging/Kconfig | 2 | ||||
-rw-r--r-- | drivers/staging/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/omaprpc/Kconfig | 15 | ||||
-rw-r--r-- | drivers/staging/omaprpc/Makefile | 17 | ||||
-rw-r--r-- | drivers/staging/omaprpc/omap_rpc.c | 1244 | ||||
-rw-r--r-- | drivers/staging/omaprpc/omap_rpc.h | 287 | ||||
-rw-r--r-- | drivers/staging/omaprpc/omap_rpc_dmabuf.c | 436 | ||||
-rw-r--r-- | drivers/staging/omaprpc/omap_rpc_internal.h | 218 | ||||
-rw-r--r-- | drivers/staging/omaprpc/omap_rpc_ion.c | 335 | ||||
-rw-r--r-- | drivers/staging/omaprpc/omap_rpc_rproc.c | 103 | ||||
-rw-r--r-- | drivers/staging/omaprpc/omap_rpc_tiler.c | 62 | ||||
-rw-r--r-- | include/linux/Kbuild | 1 | ||||
-rw-r--r-- | include/linux/omap_rpc.h | 287 |
13 files changed, 3008 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 8f6cae55..eb6a319 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -164,4 +164,6 @@ source "drivers/staging/nvec/Kconfig" source "drivers/staging/thermal_framework/Kconfig" +source "drivers/staging/omaprpc/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 1f7892c..3b39915 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -73,3 +73,4 @@ obj-$(CONFIG_DRM_PSB) += gma500/ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_MFD_NVEC) += nvec/ obj-$(CONFIG_THERMAL_FRAMEWORK) += thermal_framework/ +obj-$(CONFIG_RPC_OMAP) += omaprpc/ diff --git a/drivers/staging/omaprpc/Kconfig b/drivers/staging/omaprpc/Kconfig new file mode 100644 index 0000000..73b8043 --- /dev/null +++ b/drivers/staging/omaprpc/Kconfig @@ -0,0 +1,15 @@ + +config RPC_OMAP + tristate "OMAP Remote Procedure Call driver" + default y + 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 0000000..a02ed9e --- /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 0000000..ba80875 --- /dev/null +++ b/drivers/staging/omaprpc/omap_rpc.c @@ -0,0 +1,1244 @@ +/* + * 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); + +#if defined(OMAPRPC_PERF_MEASUREMENT) +static struct timeval start_time; +static struct timeval end_time; +static long usec_elapsed; +#endif + +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_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_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_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 defined(OMAPRPC_VERY_VERBOSE) + print_hex_dump(KERN_DEBUG, "OMAPRPC: RX: ", + DUMP_PREFIX_NONE, 16, 1, data, len, true); +#endif + 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_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_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 defined(OMAPRPC_PERF_MEASUREMENT) + do_gettimeofday(&end_time); + usec_elapsed = (end_time.tv_sec - start_time.tv_sec) * + 1000000 + end_time.tv_usec - start_time.tv_usec; + OMAPRPC_INFO(rpc->rpcserv->dev, + "write to callback took %lu usec\n", usec_elapsed); +#endif + 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 defined(OMAPRPC_PERF_MEASUREMENT) + /* capture the time delay between callback and read */ + do_gettimeofday(&start_time); +#endif + 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_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_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_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_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_INFO(rpcserv->dev, + "OMAPRPC: Instance %p has been deleted!\n", + rpc); + + if (list_empty(&rpcserv->instance_list)) { + OMAPRPC_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 defined(OMAPRPC_PERF_MEASUREMENT) + do_gettimeofday(&end_time); + usec_elapsed = (end_time.tv_sec - start_time.tv_sec) * + 1000000 + end_time.tv_usec - start_time.tv_usec; + OMAPRPC_INFO(rpc->rpcserv->dev, + "callback to read took %lu usec\n", + usec_elapsed); +#endif + + /* 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_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; + 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; + } + } + +#if defined(OMAPRPC_VERY_VERBOSE) /* Very Verbose Debugging Code */ + print_hex_dump(KERN_DEBUG, "OMAPRPC: TX: ", + DUMP_PREFIX_NONE, 16, 1, kbuf, use, true); +#endif + + /* 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; + } + +#if defined(OMAPRPC_PERF_MEASUREMENT) + /* capture the time delay between write and callback */ + do_gettimeofday(&start_time); +#endif + + /* 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_INFO(rpcserv->dev, + "OMAPRPC: Send 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_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_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_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_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_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_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_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!\n", + ret); + 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 0000000..5a5000e --- /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 0000000..0f0e0c6 --- /dev/null +++ b/drivers/staging/omaprpc/omap_rpc_dmabuf.c @@ -0,0 +1,436 @@ +/* + * 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 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_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_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_INFO(rpc->rpcserv->dev, "Pining with FD %u\n", dma->fd); + dma->dbuf = dma_buf_get((int)reserved); + if (!(IS_ERR(dma->dbuf))) { + OMAPRPC_INFO(rpc->rpcserv->dev, "DMA_BUF=%p\n", dma->dbuf); + dma->attach = dma_buf_attach(dma->dbuf, rpc->rpcserv->dev); + OMAPRPC_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_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_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_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 dam 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_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_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_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_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_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_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_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_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; + } + } + 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 0000000..6e61d49 --- /dev/null +++ b/drivers/staging/omaprpc/omap_rpc_internal.h @@ -0,0 +1,218 @@ +/* + * 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 + +/* Testing and debugging defines, leave undef'd in production */ +#undef OMAPRPC_DEBUGGING +#undef OMAPRPC_VERY_VERBOSE +#undef OMAPRPC_PERF_MEASUREMENT + +#if defined(OMAPRPC_DEBUGGING) +#define OMAPRPC_INFO(dev, fmt, ...) dev_info(dev, fmt, ## __VA_ARGS__) +#define OMAPRPC_ERR(dev, fmt, ...) dev_err(dev, fmt, ## __VA_ARGS__) +#else +#define OMAPRPC_INFO(dev, fmt, ...) +#define OMAPRPC_ERR(dev, fmt, ...) dev_err(dev, fmt, ## __VA_ARGS__) +#endif + +#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 0000000..d1447b9 --- /dev/null +++ b/drivers/staging/omaprpc/omap_rpc_ion.c @@ -0,0 +1,335 @@ +/* + * 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_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_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_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_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_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_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_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_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_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 0000000..33fd0aa --- /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 0000000..8528bed --- /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; +} + diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 01f6362..110e9cc 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -280,6 +280,7 @@ header-y += nubus.h header-y += nvram.h header-y += omap3isp.h header-y += omapfb.h +header-y += omap_rpc.h header-y += oom.h header-y += param.h header-y += parport.h diff --git a/include/linux/omap_rpc.h b/include/linux/omap_rpc.h new file mode 100644 index 0000000..5a5000e --- /dev/null +++ b/include/linux/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_ */ |