summaryrefslogtreecommitdiff
path: root/arch/arm/plat-omap/dmm_user.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-omap/dmm_user.c')
-rw-r--r--arch/arm/plat-omap/dmm_user.c288
1 files changed, 288 insertions, 0 deletions
diff --git a/arch/arm/plat-omap/dmm_user.c b/arch/arm/plat-omap/dmm_user.c
new file mode 100644
index 00000000000..da4f41579c2
--- /dev/null
+++ b/arch/arm/plat-omap/dmm_user.c
@@ -0,0 +1,288 @@
+/*
+ * OMAP DMM (Dynamic memory mapping) to IOMMU module
+ *
+ * Copyright (C) 2010 Texas Instruments. All rights reserved.
+ *
+ * Authors: Hari Kanigeri <h-kanigeri2@ti.com>
+ * Ramesh Gupta <grgupta@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/swap.h>
+#include <linux/genalloc.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+#include <plat/iommu.h>
+#include <plat/iovmm.h>
+#include <plat/dmm_user.h>
+
+
+#define OMAP_DMM_NAME "iovmm-omap"
+
+static atomic_t num_of_iovmmus;
+static struct class *omap_dmm_class;
+static dev_t omap_dmm_dev;
+
+static long omap_dmm_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long args)
+{
+ struct iodmm_struct *obj;
+ int ret = 0;
+ obj = (struct iodmm_struct *)filp->private_data;
+
+ if (!obj)
+ return -EINVAL;
+
+ if (_IOC_TYPE(cmd) != DMM_IOC_MAGIC)
+ return -ENOTTY;
+
+ switch (cmd) {
+ case DMM_IOCSETTLBENT:
+ /* FIXME: re-visit this check to perform
+ proper permission checks */
+ /* if (!capable(CAP_SYS_ADMIN))
+ return -EPERM; */
+ ret = program_tlb_entry(obj, (const void __user *)args);
+ break;
+ case DMM_IOCCREATEPOOL:
+ /* FIXME: re-visit this check to perform
+ proper permission checks */
+ /* if (!capable(CAP_SYS_ADMIN))
+ return -EPERM; */
+ ret = omap_create_dmm_pool(obj, (const void __user *)args);
+ break;
+ case DMM_IOCMEMMAP:
+ ret = dmm_user(obj, (void __user *)args);
+ break;
+ case DMM_IOCMEMUNMAP:
+ ret = user_un_map(obj, (const void __user *)args);
+ break;
+ case IOMMU_IOCEVENTREG:
+ ret = register_mmufault(obj, (const void __user *)args);
+ break;
+ case IOMMU_IOCEVENTUNREG:
+ ret = unregister_mmufault(obj, (const void __user *)args);
+ break;
+ case DMM_IOCMEMFLUSH:
+ ret = proc_begin_dma(obj, (void __user *)args);
+ break;
+ case DMM_IOCMEMINV:
+ ret = proc_end_dma(obj, (void __user *)args);
+ break;
+ /* This ioctl can be deprecated */
+ case DMM_IOCDELETEPOOL:
+ break;
+ case DMM_IOCDATOPA:
+ default:
+ return -ENOTTY;
+ }
+
+ return ret;
+}
+
+static int omap_dmm_open(struct inode *inode, struct file *filp)
+{
+ struct iodmm_struct *iodmm;
+ struct iovmm_device *obj;
+
+ obj = container_of(inode->i_cdev, struct iovmm_device, cdev);
+ obj->refcount++;
+
+ iodmm = kzalloc(sizeof(struct iodmm_struct), GFP_KERNEL);
+ INIT_LIST_HEAD(&iodmm->map_list);
+
+ iodmm->iovmm = obj;
+ obj->iommu = iommu_get(obj->name);
+ filp->private_data = iodmm;
+
+ return 0;
+}
+
+static int omap_dmm_release(struct inode *inode, struct file *filp)
+{
+ int status = 0;
+ struct iodmm_struct *obj;
+
+ if (!filp->private_data) {
+ status = -EIO;
+ goto err_out;
+ }
+ obj = filp->private_data;
+
+ flush_signals(current);
+
+ status = mutex_lock_interruptible(&obj->iovmm->dmm_map_lock);
+ if (status == 0) {
+ /*
+ * Report to remote Processor of the cleanup of these
+ * resources before cleaning in order to avoid MMU fault
+ * type of behavior
+ */
+ if (!list_empty(&obj->map_list)) {
+ iommu_notify_event(obj->iovmm->iommu, IOMMU_CLOSE,
+ NULL);
+ }
+ mutex_unlock(&obj->iovmm->dmm_map_lock);
+ } else {
+ pr_err("%s mutex_lock_interruptible returned 0x%x\n",
+ __func__, status);
+ }
+
+ user_remove_resources(obj);
+ iommu_put(obj->iovmm->iommu);
+
+ /* Delete all the DMM pools after the reference count goes to zero */
+ if (--obj->iovmm->refcount == 0)
+ omap_delete_dmm_pools(obj);
+
+ kfree(obj);
+
+ filp->private_data = NULL;
+
+err_out:
+ return status;
+}
+
+static const struct file_operations omap_dmm_fops = {
+ .owner = THIS_MODULE,
+ .open = omap_dmm_open,
+ .release = omap_dmm_release,
+ .unlocked_ioctl = omap_dmm_ioctl,
+};
+
+static int __devinit omap_dmm_probe(struct platform_device *pdev)
+{
+ int err = -ENODEV;
+ int major, minor;
+ struct device *tmpdev;
+ struct iommu_platform_data *pdata =
+ (struct iommu_platform_data *)pdev->dev.platform_data;
+ int ret = 0;
+ struct iovmm_device *obj;
+
+ obj = kzalloc(sizeof(struct iovmm_device), GFP_KERNEL);
+
+ major = MAJOR(omap_dmm_dev);
+ minor = atomic_read(&num_of_iovmmus);
+ atomic_inc(&num_of_iovmmus);
+
+ obj->minor = minor;
+ obj->name = pdata->name;
+ INIT_LIST_HEAD(&obj->mmap_pool);
+
+ cdev_init(&obj->cdev, &omap_dmm_fops);
+ obj->cdev.owner = THIS_MODULE;
+ ret = cdev_add(&obj->cdev, MKDEV(major, minor), 1);
+
+ if (ret) {
+ dev_err(&pdev->dev, "%s: cdev_add failed: %d\n", __func__, ret);
+ goto err_cdev;
+ }
+ tmpdev = device_create(omap_dmm_class, NULL,
+ MKDEV(major, minor),
+ NULL,
+ OMAP_DMM_NAME "%d", minor);
+ if (IS_ERR(tmpdev)) {
+ ret = PTR_ERR(tmpdev);
+ pr_err("%s: device_create failed: %d\n", __func__, ret);
+ goto clean_cdev;
+ }
+
+ pr_info("%s initialized %s, major: %d, base-minor: %d\n",
+ OMAP_DMM_NAME,
+ pdata->name,
+ MAJOR(omap_dmm_dev),
+ minor);
+
+ mutex_init(&obj->dmm_map_lock);
+ platform_set_drvdata(pdev, obj);
+ return 0;
+
+clean_cdev:
+ cdev_del(&obj->cdev);
+err_cdev:
+ return err;
+}
+
+static int __devexit omap_dmm_remove(struct platform_device *pdev)
+{
+ struct iovmm_device *obj = platform_get_drvdata(pdev);
+ int major = MAJOR(omap_dmm_dev);
+
+ device_destroy(omap_dmm_class, MKDEV(major, obj->minor));
+ cdev_del(&obj->cdev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(obj);
+
+ return 0;
+
+}
+
+static struct platform_driver omap_dmm_driver = {
+ .probe = omap_dmm_probe,
+ .remove = __devexit_p(omap_dmm_remove),
+ .driver = {
+ .name = "omap-iovmm",
+ },
+};
+
+static int __init dmm_user_init(void)
+{
+ int num, ret;
+
+ num = iommu_get_plat_data_size();
+
+ ret = alloc_chrdev_region(&omap_dmm_dev, 0, num, OMAP_DMM_NAME);
+ if (ret) {
+ pr_err("%s: alloc_chrdev_region failed: %d\n", __func__, ret);
+ goto out;
+ }
+ omap_dmm_class = class_create(THIS_MODULE, OMAP_DMM_NAME);
+ if (IS_ERR(omap_dmm_class)) {
+ ret = PTR_ERR(omap_dmm_class);
+ pr_err("%s: class_create failed: %d\n", __func__, ret);
+ goto unreg_region;
+ }
+ atomic_set(&num_of_iovmmus, 0);
+
+ return platform_driver_register(&omap_dmm_driver);
+unreg_region:
+ unregister_chrdev_region(omap_dmm_dev, num);
+out:
+ return ret;
+}
+module_init(dmm_user_init);
+
+static void __exit dmm_user_exit(void)
+{
+ int num = iommu_get_plat_data_size();
+
+ class_destroy(omap_dmm_class);
+ unregister_chrdev_region(omap_dmm_dev, num);
+ platform_driver_unregister(&omap_dmm_driver);
+}
+module_exit(dmm_user_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Userspace DMM to IOMMU");
+MODULE_AUTHOR("Hari Kanigeri");
+MODULE_AUTHOR("Ramesh Gupta");