mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 10:41:38 -04:00
Merge branches 'x86/vt-d', 'arm/omap', 'core', 'x86/amd' and 'arm/smmu' into next
This commit is contained in:
commit
01ce784acf
172 changed files with 3480 additions and 832 deletions
|
|
@ -261,4 +261,17 @@ config SHMOBILE_IOMMU_L1SIZE
|
|||
default 256 if SHMOBILE_IOMMU_ADDRSIZE_64MB
|
||||
default 128 if SHMOBILE_IOMMU_ADDRSIZE_32MB
|
||||
|
||||
config ARM_SMMU
|
||||
bool "ARM Ltd. System MMU (SMMU) Support"
|
||||
depends on ARM64 || (ARM_LPAE && OF)
|
||||
select IOMMU_API
|
||||
select ARM_DMA_USE_IOMMU if ARM
|
||||
help
|
||||
Support for implementations of the ARM System MMU architecture
|
||||
versions 1 and 2. The driver supports both v7l and v8l table
|
||||
formats with 4k and 64k page sizes.
|
||||
|
||||
Say Y here if your SoC includes an IOMMU device implementing
|
||||
the ARM SMMU architecture.
|
||||
|
||||
endif # IOMMU_SUPPORT
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ obj-$(CONFIG_OF_IOMMU) += of_iommu.o
|
|||
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
|
||||
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
|
||||
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
|
||||
obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
|
||||
obj-$(CONFIG_DMAR_TABLE) += dmar.o
|
||||
obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o
|
||||
obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o
|
||||
|
|
|
|||
|
|
@ -1497,6 +1497,10 @@ static unsigned long iommu_unmap_page(struct protection_domain *dom,
|
|||
|
||||
/* Large PTE found which maps this address */
|
||||
unmap_size = PTE_PAGE_SIZE(*pte);
|
||||
|
||||
/* Only unmap from the first pte in the page */
|
||||
if ((unmap_size - 1) & bus_addr)
|
||||
break;
|
||||
count = PAGE_SIZE_PTE_COUNT(unmap_size);
|
||||
for (i = 0; i < count; i++)
|
||||
pte[i] = 0ULL;
|
||||
|
|
@ -1506,7 +1510,7 @@ static unsigned long iommu_unmap_page(struct protection_domain *dom,
|
|||
unmapped += unmap_size;
|
||||
}
|
||||
|
||||
BUG_ON(!is_power_of_2(unmapped));
|
||||
BUG_ON(unmapped && !is_power_of_2(unmapped));
|
||||
|
||||
return unmapped;
|
||||
}
|
||||
|
|
@ -1906,34 +1910,59 @@ static void domain_id_free(int id)
|
|||
write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
|
||||
}
|
||||
|
||||
#define DEFINE_FREE_PT_FN(LVL, FN) \
|
||||
static void free_pt_##LVL (unsigned long __pt) \
|
||||
{ \
|
||||
unsigned long p; \
|
||||
u64 *pt; \
|
||||
int i; \
|
||||
\
|
||||
pt = (u64 *)__pt; \
|
||||
\
|
||||
for (i = 0; i < 512; ++i) { \
|
||||
if (!IOMMU_PTE_PRESENT(pt[i])) \
|
||||
continue; \
|
||||
\
|
||||
p = (unsigned long)IOMMU_PTE_PAGE(pt[i]); \
|
||||
FN(p); \
|
||||
} \
|
||||
free_page((unsigned long)pt); \
|
||||
}
|
||||
|
||||
DEFINE_FREE_PT_FN(l2, free_page)
|
||||
DEFINE_FREE_PT_FN(l3, free_pt_l2)
|
||||
DEFINE_FREE_PT_FN(l4, free_pt_l3)
|
||||
DEFINE_FREE_PT_FN(l5, free_pt_l4)
|
||||
DEFINE_FREE_PT_FN(l6, free_pt_l5)
|
||||
|
||||
static void free_pagetable(struct protection_domain *domain)
|
||||
{
|
||||
int i, j;
|
||||
u64 *p1, *p2, *p3;
|
||||
unsigned long root = (unsigned long)domain->pt_root;
|
||||
|
||||
p1 = domain->pt_root;
|
||||
|
||||
if (!p1)
|
||||
return;
|
||||
|
||||
for (i = 0; i < 512; ++i) {
|
||||
if (!IOMMU_PTE_PRESENT(p1[i]))
|
||||
continue;
|
||||
|
||||
p2 = IOMMU_PTE_PAGE(p1[i]);
|
||||
for (j = 0; j < 512; ++j) {
|
||||
if (!IOMMU_PTE_PRESENT(p2[j]))
|
||||
continue;
|
||||
p3 = IOMMU_PTE_PAGE(p2[j]);
|
||||
free_page((unsigned long)p3);
|
||||
}
|
||||
|
||||
free_page((unsigned long)p2);
|
||||
switch (domain->mode) {
|
||||
case PAGE_MODE_NONE:
|
||||
break;
|
||||
case PAGE_MODE_1_LEVEL:
|
||||
free_page(root);
|
||||
break;
|
||||
case PAGE_MODE_2_LEVEL:
|
||||
free_pt_l2(root);
|
||||
break;
|
||||
case PAGE_MODE_3_LEVEL:
|
||||
free_pt_l3(root);
|
||||
break;
|
||||
case PAGE_MODE_4_LEVEL:
|
||||
free_pt_l4(root);
|
||||
break;
|
||||
case PAGE_MODE_5_LEVEL:
|
||||
free_pt_l5(root);
|
||||
break;
|
||||
case PAGE_MODE_6_LEVEL:
|
||||
free_pt_l6(root);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
free_page((unsigned long)p1);
|
||||
|
||||
domain->pt_root = NULL;
|
||||
}
|
||||
|
||||
static void free_gcr3_tbl_level1(u64 *tbl)
|
||||
|
|
|
|||
1969
drivers/iommu/arm-smmu.c
Normal file
1969
drivers/iommu/arm-smmu.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -754,6 +754,38 @@ int iommu_domain_has_cap(struct iommu_domain *domain,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(iommu_domain_has_cap);
|
||||
|
||||
static size_t iommu_pgsize(struct iommu_domain *domain,
|
||||
unsigned long addr_merge, size_t size)
|
||||
{
|
||||
unsigned int pgsize_idx;
|
||||
size_t pgsize;
|
||||
|
||||
/* Max page size that still fits into 'size' */
|
||||
pgsize_idx = __fls(size);
|
||||
|
||||
/* need to consider alignment requirements ? */
|
||||
if (likely(addr_merge)) {
|
||||
/* Max page size allowed by address */
|
||||
unsigned int align_pgsize_idx = __ffs(addr_merge);
|
||||
pgsize_idx = min(pgsize_idx, align_pgsize_idx);
|
||||
}
|
||||
|
||||
/* build a mask of acceptable page sizes */
|
||||
pgsize = (1UL << (pgsize_idx + 1)) - 1;
|
||||
|
||||
/* throw away page sizes not supported by the hardware */
|
||||
pgsize &= domain->ops->pgsize_bitmap;
|
||||
|
||||
/* make sure we're still sane */
|
||||
BUG_ON(!pgsize);
|
||||
|
||||
/* pick the biggest page */
|
||||
pgsize_idx = __fls(pgsize);
|
||||
pgsize = 1UL << pgsize_idx;
|
||||
|
||||
return pgsize;
|
||||
}
|
||||
|
||||
int iommu_map(struct iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, size_t size, int prot)
|
||||
{
|
||||
|
|
@ -775,45 +807,18 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova,
|
|||
* size of the smallest page supported by the hardware
|
||||
*/
|
||||
if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) {
|
||||
pr_err("unaligned: iova 0x%lx pa 0x%lx size 0x%lx min_pagesz "
|
||||
"0x%x\n", iova, (unsigned long)paddr,
|
||||
(unsigned long)size, min_pagesz);
|
||||
pr_err("unaligned: iova 0x%lx pa 0x%pa size 0x%zx min_pagesz 0x%x\n",
|
||||
iova, &paddr, size, min_pagesz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("map: iova 0x%lx pa 0x%lx size 0x%lx\n", iova,
|
||||
(unsigned long)paddr, (unsigned long)size);
|
||||
pr_debug("map: iova 0x%lx pa 0x%pa size 0x%zx\n", iova, &paddr, size);
|
||||
|
||||
while (size) {
|
||||
unsigned long pgsize, addr_merge = iova | paddr;
|
||||
unsigned int pgsize_idx;
|
||||
size_t pgsize = iommu_pgsize(domain, iova | paddr, size);
|
||||
|
||||
/* Max page size that still fits into 'size' */
|
||||
pgsize_idx = __fls(size);
|
||||
|
||||
/* need to consider alignment requirements ? */
|
||||
if (likely(addr_merge)) {
|
||||
/* Max page size allowed by both iova and paddr */
|
||||
unsigned int align_pgsize_idx = __ffs(addr_merge);
|
||||
|
||||
pgsize_idx = min(pgsize_idx, align_pgsize_idx);
|
||||
}
|
||||
|
||||
/* build a mask of acceptable page sizes */
|
||||
pgsize = (1UL << (pgsize_idx + 1)) - 1;
|
||||
|
||||
/* throw away page sizes not supported by the hardware */
|
||||
pgsize &= domain->ops->pgsize_bitmap;
|
||||
|
||||
/* make sure we're still sane */
|
||||
BUG_ON(!pgsize);
|
||||
|
||||
/* pick the biggest page */
|
||||
pgsize_idx = __fls(pgsize);
|
||||
pgsize = 1UL << pgsize_idx;
|
||||
|
||||
pr_debug("mapping: iova 0x%lx pa 0x%lx pgsize %lu\n", iova,
|
||||
(unsigned long)paddr, pgsize);
|
||||
pr_debug("mapping: iova 0x%lx pa 0x%pa pgsize 0x%zx\n",
|
||||
iova, &paddr, pgsize);
|
||||
|
||||
ret = domain->ops->map(domain, iova, paddr, pgsize, prot);
|
||||
if (ret)
|
||||
|
|
@ -850,27 +855,26 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
|
|||
* by the hardware
|
||||
*/
|
||||
if (!IS_ALIGNED(iova | size, min_pagesz)) {
|
||||
pr_err("unaligned: iova 0x%lx size 0x%lx min_pagesz 0x%x\n",
|
||||
iova, (unsigned long)size, min_pagesz);
|
||||
pr_err("unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n",
|
||||
iova, size, min_pagesz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("unmap this: iova 0x%lx size 0x%lx\n", iova,
|
||||
(unsigned long)size);
|
||||
pr_debug("unmap this: iova 0x%lx size 0x%zx\n", iova, size);
|
||||
|
||||
/*
|
||||
* Keep iterating until we either unmap 'size' bytes (or more)
|
||||
* or we hit an area that isn't mapped.
|
||||
*/
|
||||
while (unmapped < size) {
|
||||
size_t left = size - unmapped;
|
||||
size_t pgsize = iommu_pgsize(domain, iova, size - unmapped);
|
||||
|
||||
unmapped_page = domain->ops->unmap(domain, iova, left);
|
||||
unmapped_page = domain->ops->unmap(domain, iova, pgsize);
|
||||
if (!unmapped_page)
|
||||
break;
|
||||
|
||||
pr_debug("unmapped: iova 0x%lx size %lx\n", iova,
|
||||
(unsigned long)unmapped_page);
|
||||
pr_debug("unmapped: iova 0x%lx size 0x%zx\n",
|
||||
iova, unmapped_page);
|
||||
|
||||
iova += unmapped_page;
|
||||
unmapped += unmapped_page;
|
||||
|
|
|
|||
|
|
@ -833,16 +833,15 @@ static irqreturn_t iommu_fault_handler(int irq, void *data)
|
|||
iopgd = iopgd_offset(obj, da);
|
||||
|
||||
if (!iopgd_is_table(*iopgd)) {
|
||||
dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p "
|
||||
"*pgd:px%08x\n", obj->name, errs, da, iopgd, *iopgd);
|
||||
dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p *pgd:px%08x\n",
|
||||
obj->name, errs, da, iopgd, *iopgd);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
iopte = iopte_offset(iopgd, da);
|
||||
|
||||
dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p *pgd:0x%08x "
|
||||
"pte:0x%p *pte:0x%08x\n", obj->name, errs, da, iopgd, *iopgd,
|
||||
iopte, *iopte);
|
||||
dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p *pgd:0x%08x pte:0x%p *pte:0x%08x\n",
|
||||
obj->name, errs, da, iopgd, *iopgd, iopte, *iopte);
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
|
@ -1235,14 +1234,16 @@ static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain,
|
|||
else if (iopte_is_large(*pte))
|
||||
ret = omap_iommu_translate(*pte, da, IOLARGE_MASK);
|
||||
else
|
||||
dev_err(dev, "bogus pte 0x%x, da 0x%lx", *pte, da);
|
||||
dev_err(dev, "bogus pte 0x%x, da 0x%llx", *pte,
|
||||
(unsigned long long)da);
|
||||
} else {
|
||||
if (iopgd_is_section(*pgd))
|
||||
ret = omap_iommu_translate(*pgd, da, IOSECTION_MASK);
|
||||
else if (iopgd_is_super(*pgd))
|
||||
ret = omap_iommu_translate(*pgd, da, IOSUPER_MASK);
|
||||
else
|
||||
dev_err(dev, "bogus pgd 0x%x, da 0x%lx", *pgd, da);
|
||||
dev_err(dev, "bogus pgd 0x%x, da 0x%llx", *pgd,
|
||||
(unsigned long long)da);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -95,4 +95,4 @@ static inline phys_addr_t omap_iommu_translate(u32 d, u32 va, u32 mask)
|
|||
#define iopte_offset(iopgd, da) (iopgd_page_vaddr(iopgd) + iopte_index(da))
|
||||
|
||||
#define to_iommu(dev) \
|
||||
(struct omap_iommu *)platform_get_drvdata(to_platform_device(dev))
|
||||
((struct omap_iommu *)platform_get_drvdata(to_platform_device(dev)))
|
||||
|
|
|
|||
|
|
@ -102,8 +102,8 @@ static size_t sgtable_len(const struct sg_table *sgt)
|
|||
}
|
||||
|
||||
if (i && sg->offset) {
|
||||
pr_err("%s: sg[%d] offset not allowed in internal "
|
||||
"entries\n", __func__, i);
|
||||
pr_err("%s: sg[%d] offset not allowed in internal entries\n",
|
||||
__func__, i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue