diff options
Diffstat (limited to 'drivers/iommu/mtk_iommu.c')
| -rw-r--r-- | drivers/iommu/mtk_iommu.c | 90 | 
1 files changed, 29 insertions, 61 deletions
diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c index 67a483c1a935..6fc1f5ecf91e 100644 --- a/drivers/iommu/mtk_iommu.c +++ b/drivers/iommu/mtk_iommu.c @@ -101,8 +101,6 @@  #define MTK_M4U_TO_PORT(id)		((id) & 0x1f)  struct mtk_iommu_domain { -	spinlock_t			pgtlock; /* lock for page table */ -  	struct io_pgtable_cfg		cfg;  	struct io_pgtable_ops		*iop; @@ -173,13 +171,16 @@ static void mtk_iommu_tlb_flush_all(void *cookie)  	}  } -static void mtk_iommu_tlb_add_flush_nosync(unsigned long iova, size_t size, -					   size_t granule, bool leaf, -					   void *cookie) +static void mtk_iommu_tlb_flush_range_sync(unsigned long iova, size_t size, +					   size_t granule, void *cookie)  {  	struct mtk_iommu_data *data = cookie; +	unsigned long flags; +	int ret; +	u32 tmp;  	for_each_m4u(data) { +		spin_lock_irqsave(&data->tlb_lock, flags);  		writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,  			       data->base + REG_MMU_INV_SEL); @@ -188,23 +189,10 @@ static void mtk_iommu_tlb_add_flush_nosync(unsigned long iova, size_t size,  			       data->base + REG_MMU_INVLD_END_A);  		writel_relaxed(F_MMU_INV_RANGE,  			       data->base + REG_MMU_INVALIDATE); -		data->tlb_flush_active = true; -	} -} - -static void mtk_iommu_tlb_sync(void *cookie) -{ -	struct mtk_iommu_data *data = cookie; -	int ret; -	u32 tmp; - -	for_each_m4u(data) { -		/* Avoid timing out if there's nothing to wait for */ -		if (!data->tlb_flush_active) -			return; +		/* tlb sync */  		ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE, -						tmp, tmp != 0, 10, 100000); +						tmp, tmp != 0, 10, 1000);  		if (ret) {  			dev_warn(data->dev,  				 "Partial TLB flush timed out, falling back to full flush\n"); @@ -212,35 +200,24 @@ static void mtk_iommu_tlb_sync(void *cookie)  		}  		/* Clear the CPE status */  		writel_relaxed(0, data->base + REG_MMU_CPE_DONE); -		data->tlb_flush_active = false; +		spin_unlock_irqrestore(&data->tlb_lock, flags);  	}  } -static void mtk_iommu_tlb_flush_walk(unsigned long iova, size_t size, -				     size_t granule, void *cookie) -{ -	mtk_iommu_tlb_add_flush_nosync(iova, size, granule, false, cookie); -	mtk_iommu_tlb_sync(cookie); -} - -static void mtk_iommu_tlb_flush_leaf(unsigned long iova, size_t size, -				     size_t granule, void *cookie) -{ -	mtk_iommu_tlb_add_flush_nosync(iova, size, granule, true, cookie); -	mtk_iommu_tlb_sync(cookie); -} -  static void mtk_iommu_tlb_flush_page_nosync(struct iommu_iotlb_gather *gather,  					    unsigned long iova, size_t granule,  					    void *cookie)  { -	mtk_iommu_tlb_add_flush_nosync(iova, granule, granule, true, cookie); +	struct mtk_iommu_data *data = cookie; +	struct iommu_domain *domain = &data->m4u_dom->domain; + +	iommu_iotlb_gather_add_page(domain, gather, iova, granule);  }  static const struct iommu_flush_ops mtk_iommu_flush_ops = {  	.tlb_flush_all = mtk_iommu_tlb_flush_all, -	.tlb_flush_walk = mtk_iommu_tlb_flush_walk, -	.tlb_flush_leaf = mtk_iommu_tlb_flush_leaf, +	.tlb_flush_walk = mtk_iommu_tlb_flush_range_sync, +	.tlb_flush_leaf = mtk_iommu_tlb_flush_range_sync,  	.tlb_add_page = mtk_iommu_tlb_flush_page_nosync,  }; @@ -316,8 +293,6 @@ static int mtk_iommu_domain_finalise(struct mtk_iommu_domain *dom)  {  	struct mtk_iommu_data *data = mtk_iommu_get_m4u_data(); -	spin_lock_init(&dom->pgtlock); -  	dom->cfg = (struct io_pgtable_cfg) {  		.quirks = IO_PGTABLE_QUIRK_ARM_NS |  			IO_PGTABLE_QUIRK_NO_PERMS | @@ -412,22 +387,17 @@ static void mtk_iommu_detach_device(struct iommu_domain *domain,  }  static int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova, -			 phys_addr_t paddr, size_t size, int prot) +			 phys_addr_t paddr, size_t size, int prot, gfp_t gfp)  {  	struct mtk_iommu_domain *dom = to_mtk_domain(domain);  	struct mtk_iommu_data *data = mtk_iommu_get_m4u_data(); -	unsigned long flags; -	int ret;  	/* The "4GB mode" M4U physically can not use the lower remap of Dram. */  	if (data->enable_4GB)  		paddr |= BIT_ULL(32); -	spin_lock_irqsave(&dom->pgtlock, flags); -	ret = dom->iop->map(dom->iop, iova, paddr, size, prot); -	spin_unlock_irqrestore(&dom->pgtlock, flags); - -	return ret; +	/* Synchronize with the tlb_lock */ +	return dom->iop->map(dom->iop, iova, paddr, size, prot);  }  static size_t mtk_iommu_unmap(struct iommu_domain *domain, @@ -435,25 +405,26 @@ static size_t mtk_iommu_unmap(struct iommu_domain *domain,  			      struct iommu_iotlb_gather *gather)  {  	struct mtk_iommu_domain *dom = to_mtk_domain(domain); -	unsigned long flags; -	size_t unmapsz; - -	spin_lock_irqsave(&dom->pgtlock, flags); -	unmapsz = dom->iop->unmap(dom->iop, iova, size, gather); -	spin_unlock_irqrestore(&dom->pgtlock, flags); -	return unmapsz; +	return dom->iop->unmap(dom->iop, iova, size, gather);  }  static void mtk_iommu_flush_iotlb_all(struct iommu_domain *domain)  { -	mtk_iommu_tlb_sync(mtk_iommu_get_m4u_data()); +	mtk_iommu_tlb_flush_all(mtk_iommu_get_m4u_data());  }  static void mtk_iommu_iotlb_sync(struct iommu_domain *domain,  				 struct iommu_iotlb_gather *gather)  { -	mtk_iommu_tlb_sync(mtk_iommu_get_m4u_data()); +	struct mtk_iommu_data *data = mtk_iommu_get_m4u_data(); +	size_t length = gather->end - gather->start; + +	if (gather->start == ULONG_MAX) +		return; + +	mtk_iommu_tlb_flush_range_sync(gather->start, length, gather->pgsize, +				       data);  }  static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain, @@ -461,13 +432,9 @@ static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain,  {  	struct mtk_iommu_domain *dom = to_mtk_domain(domain);  	struct mtk_iommu_data *data = mtk_iommu_get_m4u_data(); -	unsigned long flags;  	phys_addr_t pa; -	spin_lock_irqsave(&dom->pgtlock, flags);  	pa = dom->iop->iova_to_phys(dom->iop, iova); -	spin_unlock_irqrestore(&dom->pgtlock, flags); -  	if (data->enable_4GB && pa >= MTK_IOMMU_4GB_MODE_REMAP_BASE)  		pa &= ~BIT_ULL(32); @@ -733,6 +700,7 @@ static int mtk_iommu_probe(struct platform_device *pdev)  	if (ret)  		return ret; +	spin_lock_init(&data->tlb_lock);  	list_add_tail(&data->list, &m4ulist);  	if (!iommu_present(&platform_bus_type))  |