diff options
author | Hari Kanigeri <h-kanigeri2@ti.com> | 2010-05-28 19:50:54 -0500 |
---|---|---|
committer | Sebastien Jan <s-jan@ti.com> | 2010-07-20 10:48:39 +0200 |
commit | b832ec391fb0ed0854516e67c483ad0c6b6513e6 (patch) | |
tree | 48fc24e6e619f188bc7172c7af951f2ec8bbd443 /arch | |
parent | e253caebfdf071e849db60946eef8c9a73871caa (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.c | 10 | ||||
-rw-r--r-- | arch/arm/plat-omap/include/plat/iommu.h | 11 | ||||
-rw-r--r-- | arch/arm/plat-omap/iommu.c | 157 |
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); |