Skip to contentSkip to conversation

iommu/tegra-smmu: A set of small fixes

PATCH Applied Nicolin Chen 2 participants 6 messages Dec 20, 2019 – Jan 10, 2020

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

cover iommu/tegra-smmu: A set of small fixes

Hi all,

This series of patches are some small fixes for tegra-smmu, mainly tested Tegra210 with downstream kernel. As we only enabled limited clients for Tegra210 on mainline tree, I am not sure how critical these fixes are, so not CCing stable tree.

Nicolin Chen (4):
memory: tegra: Correct reset value of xusb_hostr iommu/tegra-smmu: Do not use PAGE_SHIFT and PAGE_MASK iommu/tegra-smmu: Fix iova->phy translation iommu/tegra-smmu: Prevent race condition between map and unmap

 drivers/iommu/tegra-smmu.c      | 29 ++++++++++++++++++++++++-----
 drivers/memory/tegra/tegra210.c |  2 +-
 2 files changed, 25 insertions(+), 6 deletions(-)
Signature · 1 line
2.17.1
#1 lore raw

PATCH 1/4 memory: tegra: Correct reset value of xusb_hostr

According to Tegra X1 (Tegra210) TRM, the reset value of xusb_hostr field (bit [7:0]) should be 0x7a. So this patch simply corrects it.

---
 drivers/memory/tegra/tegra210.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/memory/tegra/tegra210.c +1 -1
index a3918a96467f..eab4bc01c8bc 100644
@@ -436,7 +436,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
 			.reg = 0x37c,
 			.shift = 0,
 			.mask = 0xff,
-			.def = 0x39,
+			.def = 0x7a,
 		},
 	}, {
 		.id = 0x4b,
Signature · 1 line
2.17.1
#2 lore raw

Re: [PATCH 1/4] memory: tegra: Correct reset value of xusb_hostr

Quoted 7 lines · Nicolin Chen

According to Tegra X1 (Tegra210) TRM, the reset value of xusb_hostr field (bit [7:0]) should be 0x7a. So this patch simply corrects it.

---
 drivers/memory/tegra/tegra210.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Applied to for-5.6/memory, thanks.

Thierry

#3 lore raw

PATCH 2/4 iommu/tegra-smmu: Do not use PAGE_SHIFT and PAGE_MASK

In reply to Nicolin Chen

PAGE_SHIFT and PAGE_MASK are defined corresponding to the page size for CPU virtual addresses, which means PAGE_SHIFT could be a number other than 12, but tegra-smmu maintains fixed 4KB IOVA pages and has fixed [21:12] bit range for PTE entries.

So this patch replaces all PAGE_SHIFT/PAGE_MASK references with the macros defined with SMMU_PTE_SHIFT.

---
 drivers/iommu/tegra-smmu.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)
drivers/iommu/tegra-smmu.c +10 -4
index 63a147b623e6..5594b47a88bf 100644
@@ -127,6 +127,11 @@ static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset)
 #define SMMU_PDE_SHIFT 22
 #define SMMU_PTE_SHIFT 12
 
+#define SMMU_PAGE_MASK		(~(SMMU_SIZE_PT-1))
+#define SMMU_OFFSET_IN_PAGE(x)	((unsigned long)(x) & ~SMMU_PAGE_MASK)
+#define SMMU_PFN_PHYS(x)	((phys_addr_t)(x) << SMMU_PTE_SHIFT)
+#define SMMU_PHYS_PFN(x)	((unsigned long)((x) >> SMMU_PTE_SHIFT))
+
 #define SMMU_PD_READABLE	(1 << 31)
 #define SMMU_PD_WRITABLE	(1 << 30)
 #define SMMU_PD_NONSECURE	(1 << 29)
@@ -644,7 +649,7 @@ static void tegra_smmu_set_pte(struct tegra_smmu_as *as, unsigned long iova,
 			       u32 *pte, dma_addr_t pte_dma, u32 val)
 {
 	struct tegra_smmu *smmu = as->smmu;
-	unsigned long offset = offset_in_page(pte);
+	unsigned long offset = SMMU_OFFSET_IN_PAGE(pte);
 
 	*pte = val;
 
@@ -680,7 +685,7 @@ static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
 		pte_attrs |= SMMU_PTE_WRITABLE;
 
 	tegra_smmu_set_pte(as, iova, pte, pte_dma,
-			   __phys_to_pfn(paddr) | pte_attrs);
+			   SMMU_PHYS_PFN(paddr) | pte_attrs);
 
 	return 0;
 }
@@ -716,7 +721,7 @@ static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain,
 
 	pfn = *pte & as->smmu->pfn_mask;
 
-	return PFN_PHYS(pfn);
+	return SMMU_PFN_PHYS(pfn);
 }
 
 static struct tegra_smmu *tegra_smmu_find(struct device_node *np)
@@ -1034,7 +1039,8 @@ 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;
+	smmu->pfn_mask =
+		BIT_MASK(mc->soc->num_address_bits - SMMU_PTE_SHIFT) - 1;
 	dev_dbg(dev, "address bits: %u, PFN mask: %#lx\n",
 		mc->soc->num_address_bits, smmu->pfn_mask);
 	smmu->tlb_mask = (smmu->soc->num_tlb_lines << 1) - 1;
Signature · 1 line
2.17.1
#4 lore raw

PATCH 3/4 iommu/tegra-smmu: Fix iova->phys translation

In reply to Nicolin Chen

IOVA might not be always 4KB aligned. So tegra_smmu_iova_to_phys function needs to add on the lower 12-bit offset from input iova.

---
 drivers/iommu/tegra-smmu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iommu/tegra-smmu.c +1 -1
index 5594b47a88bf..3999ecb63cfa 100644
@@ -721,7 +721,7 @@ static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain,
 
 	pfn = *pte & as->smmu->pfn_mask;
 
-	return SMMU_PFN_PHYS(pfn);
+	return SMMU_PFN_PHYS(pfn) + SMMU_OFFSET_IN_PAGE(iova);
 }
 
 static struct tegra_smmu *tegra_smmu_find(struct device_node *np)
Signature · 1 line
2.17.1
#5 lore raw

PATCH 4/4 iommu/tegra-smmu: Prevent race condition between map and unmap

In reply to Nicolin Chen

When testing with ethernet downloading, "EMEM address decode error" happens due to race condition between map() and unmap() functions.

This patch adds a spin lock to protect content within as->[count] and as->pts[pde] references, since a function call might be atomic.

---
 drivers/iommu/tegra-smmu.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)
drivers/iommu/tegra-smmu.c +14 -1
index 3999ecb63cfa..236bc6d6d238 100644
@@ -37,6 +37,7 @@ struct tegra_smmu {
 
 	unsigned long *asids;
 	struct mutex lock;
+	spinlock_t as_lock;
 
 	struct list_head list;
 
@@ -664,17 +665,23 @@ static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
 			  phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
 	struct tegra_smmu_as *as = to_smmu_as(domain);
+	struct tegra_smmu *smmu = as->smmu;
+	unsigned long flags;
 	dma_addr_t pte_dma;
 	u32 pte_attrs;
 	u32 *pte;
 
+	spin_lock_irqsave(&smmu->as_lock, flags);
 	pte = as_get_pte(as, iova, &pte_dma);
-	if (!pte)
+	if (!pte) {
+		spin_unlock_irqrestore(&smmu->as_lock, flags);
 		return -ENOMEM;
+	}
 
 	/* If we aren't overwriting a pre-existing entry, increment use */
 	if (*pte == 0)
 		tegra_smmu_pte_get_use(as, iova);
+	spin_unlock_irqrestore(&smmu->as_lock, flags);
 
 	pte_attrs = SMMU_PTE_NONSECURE;
 
@@ -694,6 +701,8 @@ static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
 			       size_t size, struct iommu_iotlb_gather *gather)
 {
 	struct tegra_smmu_as *as = to_smmu_as(domain);
+	struct tegra_smmu *smmu = as->smmu;
+	unsigned long flags;
 	dma_addr_t pte_dma;
 	u32 *pte;
 
@@ -702,7 +711,10 @@ static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
 		return 0;
 
 	tegra_smmu_set_pte(as, iova, pte, pte_dma, 0);
+
+	spin_lock_irqsave(&smmu->as_lock, flags);
 	tegra_smmu_pte_put_use(as, iova);
+	spin_unlock_irqrestore(&smmu->as_lock, flags);
 
 	return size;
 }
@@ -1033,6 +1045,7 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
 
 	INIT_LIST_HEAD(&smmu->groups);
 	mutex_init(&smmu->lock);
+	spin_lock_init(&smmu->as_lock);
 
 	smmu->regs = mc->regs;
 	smmu->soc = soc;
Signature · 1 line
2.17.1