diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c index 98e30034316e..131a7a5d55d6 100644 --- a/drivers/iommu/rockchip-iommu.c +++ b/drivers/iommu/rockchip-iommu.c @@ -92,6 +92,8 @@ struct rk_iommu_domain { spinlock_t iommus_lock; /* lock for iommus list */ spinlock_t dt_lock; /* lock for modifying page directory table */ bool shootdown_entire; + struct third_iommu_ops_wrap *opt_ops; + struct device *iommu_dev; struct iommu_domain domain; }; @@ -122,6 +124,7 @@ struct rk_iommu { struct iommu_domain *domain; /* domain to which iommu is attached */ struct iommu_group *group; bool shootdown_entire; + struct third_iommu_ops_wrap *opt_ops; bool iommu_enabled; bool need_res_map; }; @@ -804,6 +807,10 @@ static phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain, u32 dte, pte; u32 *page_table; + if (rk_domain->opt_ops && rk_domain->opt_ops->iova_to_phys) + return rk_domain->opt_ops->iova_to_phys(domain, iova, + rk_domain->iommu_dev); + spin_lock_irqsave(&rk_domain->dt_lock, flags); dte = rk_domain->dt[rk_iova_dte_index(iova)]; @@ -1005,6 +1012,10 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova, u32 dte, pte_index; int ret; + if (rk_domain->opt_ops && rk_domain->opt_ops->map) + return rk_domain->opt_ops->map(domain, _iova, paddr, size, prot, + gfp, rk_domain->iommu_dev); + spin_lock_irqsave(&rk_domain->dt_lock, flags); /* @@ -1044,6 +1055,10 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova, size_t unmap_size; struct rk_iommu *iommu = rk_iommu_get(rk_domain); + if (rk_domain->opt_ops && rk_domain->opt_ops->unmap) + return rk_domain->opt_ops->unmap(domain, _iova, size, gather, + rk_domain->iommu_dev); + spin_lock_irqsave(&rk_domain->dt_lock, flags); /* @@ -1081,6 +1096,10 @@ static void rk_iommu_flush_tlb_all(struct iommu_domain *domain) unsigned long flags; int i; + if (rk_domain->opt_ops && rk_domain->opt_ops->flush_iotlb_all) + return rk_domain->opt_ops->flush_iotlb_all(domain, + rk_domain->iommu_dev); + spin_lock_irqsave(&rk_domain->iommus_lock, flags); list_for_each(pos, &rk_domain->iommus) { struct rk_iommu *iommu; @@ -1132,11 +1151,18 @@ static void rk_iommu_disable(struct rk_iommu *iommu) int rockchip_iommu_disable(struct device *dev) { struct rk_iommu *iommu; + struct rk_iommu_domain *rk_domain; iommu = rk_iommu_from_dev(dev); if (!iommu) return -ENODEV; + if (iommu->domain) { + rk_domain = to_rk_domain(iommu->domain); + if (rk_domain->opt_ops && rk_domain->opt_ops->disable) + return rk_domain->opt_ops->disable(rk_domain->iommu_dev); + } + rk_iommu_disable(iommu); return 0; @@ -1191,11 +1217,18 @@ out_disable_clocks: int rockchip_iommu_enable(struct device *dev) { struct rk_iommu *iommu; + struct rk_iommu_domain *rk_domain; iommu = rk_iommu_from_dev(dev); if (!iommu) return -ENODEV; + if (iommu->domain) { + rk_domain = to_rk_domain(iommu->domain); + if (rk_domain->opt_ops && rk_domain->opt_ops->enable) + return rk_domain->opt_ops->enable(rk_domain->iommu_dev); + } + return rk_iommu_enable(iommu); } EXPORT_SYMBOL(rockchip_iommu_enable); @@ -1247,6 +1280,15 @@ static void rk_iommu_detach_device(struct iommu_domain *domain, if (!iommu) return; + if (rk_domain->opt_ops && rk_domain->opt_ops->detach_dev) { + iommu->domain = NULL; + spin_lock_irqsave(&rk_domain->iommus_lock, flags); + list_del_init(&iommu->node); + spin_unlock_irqrestore(&rk_domain->iommus_lock, flags); + return rk_domain->opt_ops->detach_dev(domain, + rk_domain->iommu_dev); + } + dev_dbg(dev, "Detaching from iommu domain\n"); if (!iommu->domain) @@ -1282,6 +1324,20 @@ static int rk_iommu_attach_device(struct iommu_domain *domain, if (!iommu) return 0; + if (iommu->opt_ops) { + rk_domain->opt_ops = iommu->opt_ops; + rk_domain->iommu_dev = iommu->dev; + } + + if (rk_domain->opt_ops && rk_domain->opt_ops->attach_dev) { + iommu->domain = domain; + spin_lock_irqsave(&rk_domain->iommus_lock, flags); + list_add_tail(&iommu->node, &rk_domain->iommus); + spin_unlock_irqrestore(&rk_domain->iommus_lock, flags); + return rk_domain->opt_ops->attach_dev(domain, + rk_domain->iommu_dev); + } + dev_dbg(dev, "Attaching to iommu domain\n"); if (iommu->domain) @@ -1364,6 +1420,9 @@ static void rk_iommu_domain_free(struct iommu_domain *domain) struct rk_iommu_domain *rk_domain = to_rk_domain(domain); int i; + if (rk_domain->opt_ops && rk_domain->opt_ops->free) + rk_domain->opt_ops->free(domain, rk_domain->iommu_dev); + WARN_ON(!list_empty(&rk_domain->iommus)); for (i = 0; i < NUM_DT_ENTRIES; i++) { @@ -1497,6 +1556,7 @@ static const struct iommu_ops rk_iommu_ops = { .device_group = rk_iommu_device_group, .pgsize_bitmap = RK_IOMMU_PGSIZE_BITMAP, .of_xlate = rk_iommu_of_xlate, + .owner = THIS_MODULE, .default_domain_ops = &(const struct iommu_domain_ops) { .attach_dev = rk_iommu_attach_device, .detach_dev = rk_iommu_detach_device, @@ -1536,6 +1596,11 @@ static int rk_iommu_probe(struct platform_device *pdev) if (WARN_ON(rk_ops != ops)) return -EINVAL; + if (device_property_match_string(dev, "compatible", "rockchip,iommu-av1d") >= 0) { + iommu->opt_ops = NULL; + goto alloc_group; + } + iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases), GFP_KERNEL); if (!iommu->bases) @@ -1592,6 +1657,7 @@ static int rk_iommu_probe(struct platform_device *pdev) if (err) return err; +alloc_group: iommu->group = iommu_group_alloc(); if (IS_ERR(iommu->group)) { err = PTR_ERR(iommu->group); @@ -1614,6 +1680,9 @@ static int rk_iommu_probe(struct platform_device *pdev) if (!dma_dev) dma_dev = &pdev->dev; + if (iommu->opt_ops) + goto opt_iommu_probe; + pm_runtime_enable(dev); if (iommu->skip_read) @@ -1640,6 +1709,13 @@ skip_request_irq: pr_info("%s,%d, res_page = 0x%pa\n", __func__, __LINE__, &res_page); } +opt_iommu_probe: + if (iommu->opt_ops && iommu->opt_ops->probe) { + err = iommu->opt_ops->probe(pdev); + if (err) + goto err_remove_sysfs; + } + dma_set_mask_and_coherent(dev, rk_ops->dma_bit_mask); return 0; @@ -1656,6 +1732,13 @@ static void rk_iommu_shutdown(struct platform_device *pdev) { struct rk_iommu *iommu = platform_get_drvdata(pdev); int i; + struct rk_iommu_domain *rk_domain; + + if (iommu->domain) { + rk_domain = to_rk_domain(iommu->domain); + if (rk_domain->opt_ops && rk_domain->opt_ops->shutdown) + return rk_domain->opt_ops->shutdown(pdev); + } if (iommu->skip_read) goto skip_free_irq; @@ -1674,10 +1757,15 @@ skip_free_irq: static int __maybe_unused rk_iommu_suspend(struct device *dev) { struct rk_iommu *iommu = dev_get_drvdata(dev); + struct rk_iommu_domain *rk_domain; if (!iommu->domain) return 0; + rk_domain = to_rk_domain(iommu->domain); + if (rk_domain->opt_ops && rk_domain->opt_ops->suspend) + return rk_domain->opt_ops->suspend(dev); + if (iommu->dlr_disable) return 0; @@ -1688,10 +1776,15 @@ static int __maybe_unused rk_iommu_suspend(struct device *dev) static int __maybe_unused rk_iommu_resume(struct device *dev) { struct rk_iommu *iommu = dev_get_drvdata(dev); + struct rk_iommu_domain *rk_domain; if (!iommu->domain) return 0; + rk_domain = to_rk_domain(iommu->domain); + if (rk_domain->opt_ops && rk_domain->opt_ops->resume) + return rk_domain->opt_ops->resume(dev); + if (iommu->dlr_disable) return 0; diff --git a/include/soc/rockchip/rockchip_iommu.h b/include/soc/rockchip/rockchip_iommu.h index 28af038a8e9b..4ed4328078b0 100644 --- a/include/soc/rockchip/rockchip_iommu.h +++ b/include/soc/rockchip/rockchip_iommu.h @@ -6,6 +6,34 @@ #define __SOC_ROCKCHIP_IOMMU_H struct device; +struct iommu_domain; +struct iommu_iotlb_gather; +struct platform_device; + +/* 1. dev and pdev is iommu device. + * 2. store third_iommu data in dev->platform_data. + */ +struct third_iommu_ops_wrap { + int (*attach_dev)(struct iommu_domain *domain, struct device *dev); + void (*detach_dev)(struct iommu_domain *domain, struct device *dev); + int (*map)(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot, gfp_t gfp, + struct device *dev); + size_t (*unmap)(struct iommu_domain *domain, unsigned long iova, + size_t size, struct iommu_iotlb_gather *iotlb_gather, + struct device *dev); + void (*flush_iotlb_all)(struct iommu_domain *domain, + struct device *dev); + phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, + dma_addr_t iova, struct device *dev); + void (*free)(struct iommu_domain *domain, struct device *dev); + int (*enable)(struct device *dev); + int (*disable)(struct device *dev); + void (*shutdown)(struct platform_device *pdev); + int (*suspend)(struct device *dev); + int (*resume)(struct device *dev); + int (*probe)(struct platform_device *pdev); +}; #if IS_ENABLED(CONFIG_ROCKCHIP_IOMMU) int rockchip_iommu_enable(struct device *dev);