diff options
author | codeworkx <codeworkx@cyanogenmod.com> | 2012-09-17 17:53:57 +0200 |
---|---|---|
committer | codeworkx <codeworkx@cyanogenmod.com> | 2012-09-18 16:31:59 +0200 |
commit | c28265764ec6ad9995eb0c761a376ffc9f141fcd (patch) | |
tree | 3ad899757480d47deb2be6011509a4243e8e0dc2 /arch/arm/plat-s5p/s5p_iommu.c | |
parent | 0ddbcb39c0dc0318f68d858f25a96a074142af2f (diff) | |
download | kernel_samsung_smdk4412-c28265764ec6ad9995eb0c761a376ffc9f141fcd.zip kernel_samsung_smdk4412-c28265764ec6ad9995eb0c761a376ffc9f141fcd.tar.gz kernel_samsung_smdk4412-c28265764ec6ad9995eb0c761a376ffc9f141fcd.tar.bz2 |
applied patches from i9305 jb sources, updated mali to r3p0
Change-Id: Iec4bc4e2fb59e2cf5b4d25568a644d4e3719565e
Diffstat (limited to 'arch/arm/plat-s5p/s5p_iommu.c')
-rw-r--r-- | arch/arm/plat-s5p/s5p_iommu.c | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/arch/arm/plat-s5p/s5p_iommu.c b/arch/arm/plat-s5p/s5p_iommu.c index a900c84..32a0de5 100644 --- a/arch/arm/plat-s5p/s5p_iommu.c +++ b/arch/arm/plat-s5p/s5p_iommu.c @@ -124,6 +124,56 @@ static void s5p_iommu_domain_destroy(struct iommu_domain *domain) domain->priv = NULL; } +#ifdef CONFIG_DRM_EXYNOS_IOMMU +static int s5p_iommu_attach_device(struct iommu_domain *domain, + struct device *dev) +{ + int ret; + struct s5p_iommu_domain *s5p_domain = domain->priv; + struct sysmmu_drvdata *data = NULL; + + mutex_lock(&s5p_domain->lock); + + /* + * get sysmmu_drvdata to dev. + * owner device was set to sysmmu->platform_data at machine code. + */ + data = get_sysmmu_data(dev, data); + if (!data) + return -EFAULT; + + mutex_unlock(&s5p_domain->lock); + + ret = s5p_sysmmu_enable(dev, virt_to_phys(s5p_domain->pgtable)); + if (ret) + return ret; + + return 0; +} + +static void s5p_iommu_detach_device(struct iommu_domain *domain, + struct device *dev) +{ + struct sysmmu_drvdata *data = NULL; + struct s5p_iommu_domain *s5p_domain = domain->priv; + + mutex_lock(&s5p_domain->lock); + + /* + * get sysmmu_drvdata to dev. + * owner device was set to sysmmu->platform_data at machine code. + */ + data = get_sysmmu_data(dev, data); + if (!data) { + dev_err(dev, "failed to detach device.\n"); + return; + } + + s5p_sysmmu_disable(dev); + + mutex_unlock(&s5p_domain->lock); +} +#else static int s5p_iommu_attach_device(struct iommu_domain *domain, struct device *dev) { @@ -168,6 +218,7 @@ static void s5p_iommu_detach_device(struct iommu_domain *domain, } } +#endif static bool section_available(struct iommu_domain *domain, unsigned long *lv1entry) @@ -349,6 +400,92 @@ mapping_done: return ret; } +#ifdef CONFIG_DRM_EXYNOS_IOMMU +static int s5p_iommu_unmap(struct iommu_domain *domain, unsigned long iova, + int gfp_order) +{ + struct s5p_iommu_domain *s5p_domain = domain->priv; + struct sysmmu_drvdata *data; + struct list_head *sysmmu_list, *pos; + unsigned long *entry; + int num_entry; + + BUG_ON(s5p_domain->pgtable == NULL); + + mutex_lock(&s5p_domain->lock); + + entry = s5p_domain->pgtable + (iova >> S5P_SECTION_SHIFT); + + if (gfp_order >= S5P_SECTION_ORDER) { + num_entry = 1 << (gfp_order - S5P_SECTION_ORDER); + while (num_entry--) { + if (S5P_SECTION_LV1_ENTRY(*entry)) { + MAKE_FAULT_ENTRY(*entry); + } else if (S5P_PAGE_LV1_ENTRY(*entry)) { + unsigned long *lv2beg, *lv2end; + lv2beg = phys_to_virt( + *entry & S5P_LV2TABLE_MASK); + lv2end = lv2beg + S5P_LV2TABLE_ENTRIES; + while (lv2beg != lv2end) { + MAKE_FAULT_ENTRY(*lv2beg); + lv2beg++; + } + } + entry++; + } + } else { + entry = GET_LV2ENTRY(*entry, iova); + + BUG_ON(S5P_LPAGE_LV2_ENTRY(*entry) && + (gfp_order < S5P_LPAGE_ORDER)); + + num_entry = 1 << gfp_order; + + while (num_entry--) { + MAKE_FAULT_ENTRY(*entry); + entry++; + } + } + + sysmmu_list = get_sysmmu_list(); + + /* + * invalidate tlb entries to iova(device address) to each iommu + * registered in sysmmu_list. + * + * P.S. a device using iommu was set to data->owner at machine code + * and enabled iommu was added in sysmmu_list at sysmmu probe + */ + list_for_each(pos, sysmmu_list) { + unsigned int page_size, count; + + /* + * get entry count and page size to device address space + * mapped with iommu page table and invalidate each entry. + */ + if (gfp_order >= S5P_SECTION_ORDER) { + count = 1 << (gfp_order - S5P_SECTION_ORDER); + page_size = S5P_SECTION_SIZE; + } else if (gfp_order >= S5P_LPAGE_ORDER) { + count = 1 << (gfp_order - S5P_LPAGE_ORDER); + page_size = S5P_LPAGE_SIZE; + } else { + count = 1 << (gfp_order - S5P_SPAGE_ORDER); + page_size = S5P_SPAGE_SIZE; + } + + data = list_entry(pos, struct sysmmu_drvdata, node); + if (data) + s5p_sysmmu_tlb_invalidate_entry(data->owner, iova, + count, page_size); + } + + mutex_unlock(&s5p_domain->lock); + + return 0; +} +#else + static int s5p_iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order) { @@ -400,6 +537,7 @@ static int s5p_iommu_unmap(struct iommu_domain *domain, unsigned long iova, return 0; } +#endif static phys_addr_t s5p_iommu_iova_to_phys(struct iommu_domain *domain, unsigned long iova) |