summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorHari Kanigeri <h-kanigeri2@ti.com>2010-05-28 19:50:54 -0500
committerSebastien Jan <s-jan@ti.com>2010-07-20 10:48:39 +0200
commitb832ec391fb0ed0854516e67c483ad0c6b6513e6 (patch)
tree48fc24e6e619f188bc7172c7af951f2ec8bbd443 /arch
parente253caebfdf071e849db60946eef8c9a73871caa (diff)
omap:iommu-char driver interface for tlb entries
This patch provides char driver interface to IOMMU to program tlb entries directly from user space. Todo: plug the reset mechanism as part of iommu_get function. Signed-off-by: Hari Kanigeri <h-kangieri2@ti.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-omap2/omap-iommu.c10
-rw-r--r--arch/arm/plat-omap/include/plat/iommu.h11
-rw-r--r--arch/arm/plat-omap/iommu.c157
3 files changed, 177 insertions, 1 deletions
diff --git a/arch/arm/mach-omap2/omap-iommu.c b/arch/arm/mach-omap2/omap-iommu.c
index f5a1aad1a5c0..8411c105c399 100644
--- a/arch/arm/mach-omap2/omap-iommu.c
+++ b/arch/arm/mach-omap2/omap-iommu.c
@@ -88,6 +88,16 @@ static struct platform_device *omap4_iommu_pdev[NR_OMAP4_IOMMU_DEVICES];
static struct platform_device **omap_iommu_pdev;
+int iommu_get_plat_data_size()
+{
+ return num_iommu_devices;
+}
+
+struct iommu_device *iommu_get_device_data(void)
+{
+ return devices;
+}
+
static int __init omap_iommu_init(void)
{
int i, err;
diff --git a/arch/arm/plat-omap/include/plat/iommu.h b/arch/arm/plat-omap/include/plat/iommu.h
index 33c7d41cb6a5..a45a0b1c348f 100644
--- a/arch/arm/plat-omap/include/plat/iommu.h
+++ b/arch/arm/plat-omap/include/plat/iommu.h
@@ -13,6 +13,9 @@
#ifndef __MACH_IOMMU_H
#define __MACH_IOMMU_H
+#include <linux/device.h>
+#include <linux/cdev.h>
+
struct iotlb_entry {
u32 da;
u32 pa;
@@ -50,6 +53,8 @@ struct iommu {
int (*isr)(struct iommu *obj);
void *ctx; /* iommu context: registres saved area */
+ struct cdev cdev;
+ int minor;
};
struct cr_regs {
@@ -105,6 +110,11 @@ struct iommu_platform_data {
const int nr_tlb_entries;
};
+#define IOMMU_IOC_MAGIC 'I'
+
+#define IOMMU_IOCSETTLBENT _IO(IOMMU_IOC_MAGIC, 0)
+
+
#if defined(CONFIG_ARCH_OMAP1)
#error "iommu for this processor not implemented yet"
#else
@@ -167,4 +177,5 @@ extern int foreach_iommu_device(void *data,
extern ssize_t iommu_dump_ctx(struct iommu *obj, char *buf, ssize_t len);
extern size_t dump_tlb_entries(struct iommu *obj, char *buf, ssize_t len);
+extern int iommu_get_plat_data_size(void);
#endif /* __MACH_IOMMU_H */
diff --git a/arch/arm/plat-omap/iommu.c b/arch/arm/plat-omap/iommu.c
index aa064e1d02e5..5d1bf0eb64e6 100644
--- a/arch/arm/plat-omap/iommu.c
+++ b/arch/arm/plat-omap/iommu.c
@@ -25,10 +25,23 @@
#include "iopgtable.h"
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+
+
#define for_each_iotlb_cr(obj, n, __i, cr) \
for (__i = 0; \
(__i < (n)) && (cr = __iotlb_read_cr((obj), __i), true); \
__i++)
+#define OMAP_IOMMU_NAME "omap-iommu"
+
+static atomic_t num_of_iommus;
+static struct class *omap_iommu_class;
+static dev_t omap_iommu_dev;
+
/* accommodate the difference between omap1 and omap2/3 */
static const struct iommu_functions *arch_iommu;
@@ -176,7 +189,6 @@ static void iotlb_lock_get(struct iommu *obj, struct iotlb_lock *l)
l->base = MMU_LOCK_BASE(val);
l->vict = MMU_LOCK_VICT(val);
-
}
static void iotlb_lock_set(struct iommu *obj, struct iotlb_lock *l)
@@ -874,6 +886,85 @@ void iommu_put(struct iommu *obj)
}
EXPORT_SYMBOL_GPL(iommu_put);
+struct omap_iommu_dev {
+ struct device *dev;
+ struct cdev cdev;
+ atomic_t count;
+ int state;
+ int minor;
+};
+
+static int omap_iommu_open(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+ struct iommu *obj;
+
+ obj = container_of(inode->i_cdev, struct iommu, cdev);
+ if (!obj->dev)
+ return -EINVAL;
+
+ filp->private_data = obj;
+ iommu_get(obj->name);
+
+ return ret;
+}
+
+static int omap_iommu_release(struct inode *inode, struct file *filp)
+{
+ struct iommu *obj;
+ obj = container_of(inode->i_cdev, struct iommu, cdev);
+ if (!obj->dev)
+ return -EINVAL;
+ iommu_put(obj);
+ return 0;
+}
+
+static int omap_iommu_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long args)
+
+{
+ struct iommu *obj;
+ int ret = 0;
+
+ obj = filp->private_data;
+ if (!obj)
+ return -EINVAL;
+
+ if (_IOC_TYPE(cmd) != IOMMU_IOC_MAGIC)
+ return -ENOTTY;
+
+ switch (cmd) {
+ case IOMMU_IOCSETTLBENT:
+ {
+ struct iotlb_entry e;
+ int size;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ size = copy_from_user(&e, (void __user *)args,
+ sizeof(struct iotlb_entry));
+ if (size) {
+ ret = -EINVAL;
+ goto err_user_buf;
+ }
+ load_iotlb_entry(obj, &e);
+ break;
+ }
+ default:
+ return -ENOTTY;
+ }
+err_user_buf:
+ return ret;
+}
+
+
+static const struct file_operations omap_iommu_fops = {
+ .owner = THIS_MODULE,
+ .open = omap_iommu_open,
+ .release = omap_iommu_release,
+ .ioctl = omap_iommu_ioctl,
+};
+
+
/*
* OMAP Device MMU(IOMMU) detection
*/
@@ -885,6 +976,9 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev)
struct iommu *obj;
struct resource *res;
struct iommu_platform_data *pdata = pdev->dev.platform_data;
+ int major, minor;
+ struct device *tmpdev;
+ int ret = 0;
if (pdev->num_resources != 2)
return -EINVAL;
@@ -948,8 +1042,42 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev)
BUG_ON(!IS_ALIGNED((unsigned long)obj->iopgd, IOPGD_TABLE_SIZE));
dev_info(&pdev->dev, "%s registered\n", obj->name);
+
+ major = MAJOR(omap_iommu_dev);
+ minor = atomic_read(&num_of_iommus);
+ atomic_inc(&num_of_iommus);
+
+ obj->minor = minor;
+
+ cdev_init(&obj->cdev, &omap_iommu_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_iommu_class, NULL,
+ MKDEV(major, minor),
+ NULL,
+ OMAP_IOMMU_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_IOMMU_NAME,
+ pdata->name,
+ MAJOR(omap_iommu_dev),
+ minor);
+
return 0;
+clean_cdev:
+ cdev_del(&obj->cdev);
+err_cdev:
err_pgd:
free_irq(irq, obj);
err_irq:
@@ -967,6 +1095,10 @@ static int __devexit omap_iommu_remove(struct platform_device *pdev)
int irq;
struct resource *res;
struct iommu *obj = platform_get_drvdata(pdev);
+ int major = MAJOR(omap_iommu_dev);
+
+ device_destroy(omap_iommu_class, MKDEV(major, obj->minor));
+ cdev_del(&obj->cdev);
platform_set_drvdata(pdev, NULL);
@@ -998,11 +1130,13 @@ static void iopte_cachep_ctor(void *iopte)
clean_dcache_area(iopte, IOPTE_TABLE_SIZE);
}
+
static int __init omap_iommu_init(void)
{
struct kmem_cache *p;
const unsigned long flags = SLAB_HWCACHE_ALIGN;
size_t align = 1 << 10; /* L2 pagetable alignement */
+ int ret, num;
p = kmem_cache_create("iopte_cache", IOPTE_TABLE_SIZE, align, flags,
iopte_cachep_ctor);
@@ -1010,7 +1144,28 @@ static int __init omap_iommu_init(void)
return -ENOMEM;
iopte_cachep = p;
+ num = iommu_get_plat_data_size();
+ ret = alloc_chrdev_region(&omap_iommu_dev, 0, num, OMAP_IOMMU_NAME);
+
+ if (ret) {
+ pr_err("%s: alloc_chrdev_region failed: %d\n", __func__, ret);
+ goto out;
+ }
+
+ omap_iommu_class = class_create(THIS_MODULE, OMAP_IOMMU_NAME);
+ if (IS_ERR(omap_iommu_class)) {
+ ret = PTR_ERR(omap_iommu_class);
+ pr_err("%s: class_create failed: %d\n", __func__, ret);
+ goto unreg_region;
+ }
+ atomic_set(&num_of_iommus, 0);
+
return platform_driver_register(&omap_iommu_driver);
+
+unreg_region:
+ unregister_chrdev_region(omap_iommu_dev, num);
+out:
+ return ret;
}
module_init(omap_iommu_init);