From d5f1a81ccb8947828f60b50d8e9ed7617259a9ec Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 26 Mar 2015 13:43:12 +0100 Subject: iommu/tegra-smmu: Make use of domain_alloc and domain_free Implement domain_alloc and domain_free iommu-ops as a replacement for domain_init/domain_destroy. Tested-by: Thierry Reding Acked-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) (limited to 'drivers/iommu/tegra-smmu.c') diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 6e134c7c227f..720829724d86 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -31,7 +31,7 @@ struct tegra_smmu { }; struct tegra_smmu_as { - struct iommu_domain *domain; + struct iommu_domain domain; struct tegra_smmu *smmu; unsigned int use_count; struct page *count; @@ -40,6 +40,11 @@ struct tegra_smmu_as { u32 attr; }; +static struct tegra_smmu_as *to_smmu_as(struct iommu_domain *dom) +{ + return container_of(dom, struct tegra_smmu_as, domain); +} + static inline void smmu_writel(struct tegra_smmu *smmu, u32 value, unsigned long offset) { @@ -224,30 +229,32 @@ static bool tegra_smmu_capable(enum iommu_cap cap) return false; } -static int tegra_smmu_domain_init(struct iommu_domain *domain) +static struct iommu_domain *tegra_smmu_domain_alloc(unsigned type) { struct tegra_smmu_as *as; unsigned int i; uint32_t *pd; + if (type != IOMMU_DOMAIN_UNMANAGED) + return NULL; + as = kzalloc(sizeof(*as), GFP_KERNEL); if (!as) - return -ENOMEM; + return NULL; as->attr = SMMU_PD_READABLE | SMMU_PD_WRITABLE | SMMU_PD_NONSECURE; - as->domain = domain; as->pd = alloc_page(GFP_KERNEL | __GFP_DMA); if (!as->pd) { kfree(as); - return -ENOMEM; + return NULL; } as->count = alloc_page(GFP_KERNEL); if (!as->count) { __free_page(as->pd); kfree(as); - return -ENOMEM; + return NULL; } /* clear PDEs */ @@ -264,14 +271,12 @@ static int tegra_smmu_domain_init(struct iommu_domain *domain) for (i = 0; i < SMMU_NUM_PDE; i++) pd[i] = 0; - domain->priv = as; - - return 0; + return &as->domain; } -static void tegra_smmu_domain_destroy(struct iommu_domain *domain) +static void tegra_smmu_domain_free(struct iommu_domain *domain) { - struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu_as *as = to_smmu_as(domain); /* TODO: free page directory and page tables */ ClearPageReserved(as->pd); @@ -395,7 +400,7 @@ static int tegra_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) { struct tegra_smmu *smmu = dev->archdata.iommu; - struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu_as *as = to_smmu_as(domain); struct device_node *np = dev->of_node; struct of_phandle_args args; unsigned int index = 0; @@ -428,7 +433,7 @@ static int tegra_smmu_attach_dev(struct iommu_domain *domain, static void tegra_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) { - struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu_as *as = to_smmu_as(domain); struct device_node *np = dev->of_node; struct tegra_smmu *smmu = as->smmu; struct of_phandle_args args; @@ -524,7 +529,7 @@ static void as_put_pte(struct tegra_smmu_as *as, dma_addr_t iova) static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t size, int prot) { - struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu_as *as = to_smmu_as(domain); struct tegra_smmu *smmu = as->smmu; unsigned long offset; struct page *page; @@ -548,7 +553,7 @@ static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova, static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) { - struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu_as *as = to_smmu_as(domain); struct tegra_smmu *smmu = as->smmu; unsigned long offset; struct page *page; @@ -572,7 +577,7 @@ static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova, static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) { - struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu_as *as = to_smmu_as(domain); struct page *page; unsigned long pfn; u32 *pte; @@ -633,8 +638,8 @@ static void tegra_smmu_remove_device(struct device *dev) static const struct iommu_ops tegra_smmu_ops = { .capable = tegra_smmu_capable, - .domain_init = tegra_smmu_domain_init, - .domain_destroy = tegra_smmu_domain_destroy, + .domain_alloc = tegra_smmu_domain_alloc, + .domain_free = tegra_smmu_domain_free, .attach_dev = tegra_smmu_attach_dev, .detach_dev = tegra_smmu_detach_dev, .add_device = tegra_smmu_add_device, -- cgit v1.2.3 From 471d9144b4ec2a2cbf497af7e186c1fe89b5150e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 27 Mar 2015 11:07:25 +0100 Subject: iommu/tegra: Setup aperture Each address space in the Tegra SMMU provides 4 GiB worth of addresses. Cc: Hiroshi Doyu Signed-off-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/iommu/tegra-smmu.c') diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 6e134c7c227f..d8418b54619e 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -266,6 +266,11 @@ static int tegra_smmu_domain_init(struct iommu_domain *domain) domain->priv = as; + /* setup aperture */ + domain->geometry.aperture_start = 0; + domain->geometry.aperture_end = 0xffffffff; + domain->geometry.force_aperture = true; + return 0; } -- cgit v1.2.3 From 804cb54cbb3ddac58218e830d64816617bdb6da8 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 27 Mar 2015 11:07:27 +0100 Subject: iommu/tegra: smmu: Compute PFN mask at runtime The SMMU on Tegra30 and Tegra114 supports addressing up to 4 GiB of physical memory. On Tegra124 the addressable physical memory was extended to 16 GiB. The page frame number stored in PTEs therefore requires 20 or 22 bits, depending on SoC generation. In order to cope with this, compute the proper value at runtime. Reported-by: Joseph Lo Cc: Hiroshi Doyu Signed-off-by: Thierry Reding Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/iommu/tegra-smmu.c') diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index d8418b54619e..76887a73b47a 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -6,6 +6,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -24,6 +25,8 @@ struct tegra_smmu { struct tegra_mc *mc; const struct tegra_smmu_soc *soc; + unsigned long pfn_mask; + unsigned long *asids; struct mutex lock; @@ -105,8 +108,6 @@ static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset) #define SMMU_PDE_SHIFT 22 #define SMMU_PTE_SHIFT 12 -#define SMMU_PFN_MASK 0x000fffff - #define SMMU_PD_READABLE (1 << 31) #define SMMU_PD_WRITABLE (1 << 30) #define SMMU_PD_NONSECURE (1 << 29) @@ -486,7 +487,7 @@ static u32 *as_get_pte(struct tegra_smmu_as *as, dma_addr_t iova, smmu_flush_tlb_section(smmu, as->id, iova); smmu_flush(smmu); } else { - page = pfn_to_page(pd[pde] & SMMU_PFN_MASK); + page = pfn_to_page(pd[pde] & smmu->pfn_mask); pt = page_address(page); } @@ -508,7 +509,7 @@ static void as_put_pte(struct tegra_smmu_as *as, dma_addr_t iova) u32 *pd = page_address(as->pd), *pt; struct page *page; - page = pfn_to_page(pd[pde] & SMMU_PFN_MASK); + page = pfn_to_page(pd[pde] & as->smmu->pfn_mask); pt = page_address(page); /* @@ -583,7 +584,7 @@ static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain, u32 *pte; pte = as_get_pte(as, iova, &page); - pfn = *pte & SMMU_PFN_MASK; + pfn = *pte & as->smmu->pfn_mask; return PFN_PHYS(pfn); } @@ -707,6 +708,10 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev, smmu->dev = dev; smmu->mc = mc; + smmu->pfn_mask = BIT_MASK(mc->soc->num_address_bits - PAGE_SHIFT) - 1; + dev_dbg(dev, "address bits: %u, PFN mask: %#lx\n", + mc->soc->num_address_bits, smmu->pfn_mask); + value = SMMU_PTC_CONFIG_ENABLE | SMMU_PTC_CONFIG_INDEX_MAP(0x3f); if (soc->supports_request_limit) -- cgit v1.2.3