qcom supports a single iommu instance with multiple ids. Introduce a per-device data to store the iommu and ids list. Allocate and initialize it with iommu_fw_alloc_per_device_ids(). Remove qcom_iommu_of_xlate(). This already was checking that all instances are the same, it is now done in common code. Convert the rest of the funcs from calling dev_iommu_fwspec_get() to using the per-device data and remove all use of fwspec. Store the per-dev data in the qcom_iommu_domain instead of the iommu and fwspec pointers. Convert the places using dev_iommu_priv_get() to use the per-device data not the iommu. Remove to_iommu(). Signed-off-by: Jason Gunthorpe <jgg@xxxxxxxxxx> --- drivers/iommu/arm/arm-smmu/qcom_iommu.c | 161 ++++++++++++------------ 1 file changed, 81 insertions(+), 80 deletions(-) diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c index 33f3c870086cea..4baca45df99971 100644 --- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c +++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c @@ -17,6 +17,7 @@ #include <linux/io-64-nonatomic-hi-lo.h> #include <linux/io-pgtable.h> #include <linux/iommu.h> +#include <linux/iommu-driver.h> #include <linux/iopoll.h> #include <linux/kconfig.h> #include <linux/init.h> @@ -54,6 +55,12 @@ struct qcom_iommu_dev { struct qcom_iommu_ctx *ctxs[]; /* indexed by asid */ }; +struct qcom_iommu_master { + struct qcom_iommu_dev *iommu; + unsigned int num_ids; + u32 ids[] __counted_by(num_ids); +}; + struct qcom_iommu_ctx { struct device *dev; void __iomem *base; @@ -68,8 +75,7 @@ struct qcom_iommu_domain { spinlock_t pgtbl_lock; struct mutex init_mutex; /* Protects iommu pointer */ struct iommu_domain domain; - struct qcom_iommu_dev *iommu; - struct iommu_fwspec *fwspec; + struct qcom_iommu_master *master; }; static struct qcom_iommu_domain *to_qcom_iommu_domain(struct iommu_domain *dom) @@ -81,7 +87,7 @@ static const struct iommu_ops qcom_iommu_ops; static struct qcom_iommu_ctx * to_ctx(struct qcom_iommu_domain *d, unsigned asid) { - struct qcom_iommu_dev *qcom_iommu = d->iommu; + struct qcom_iommu_dev *qcom_iommu = d->master->iommu; if (!qcom_iommu) return NULL; return qcom_iommu->ctxs[asid]; @@ -114,11 +120,11 @@ iommu_readq(struct qcom_iommu_ctx *ctx, unsigned reg) static void qcom_iommu_tlb_sync(void *cookie) { struct qcom_iommu_domain *qcom_domain = cookie; - struct iommu_fwspec *fwspec = qcom_domain->fwspec; + struct qcom_iommu_master *master = qcom_domain->master; unsigned i; - for (i = 0; i < fwspec->num_ids; i++) { - struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, fwspec->ids[i]); + for (i = 0; i < master->num_ids; i++) { + struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, master->ids[i]); unsigned int val, ret; iommu_writel(ctx, ARM_SMMU_CB_TLBSYNC, 0); @@ -133,11 +139,11 @@ static void qcom_iommu_tlb_sync(void *cookie) static void qcom_iommu_tlb_inv_context(void *cookie) { struct qcom_iommu_domain *qcom_domain = cookie; - struct iommu_fwspec *fwspec = qcom_domain->fwspec; + struct qcom_iommu_master *master = qcom_domain->master; unsigned i; - for (i = 0; i < fwspec->num_ids; i++) { - struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, fwspec->ids[i]); + for (i = 0; i < master->num_ids; i++) { + struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, master->ids[i]); iommu_writel(ctx, ARM_SMMU_CB_S1_TLBIASID, ctx->asid); } @@ -148,13 +154,13 @@ static void qcom_iommu_tlb_inv_range_nosync(unsigned long iova, size_t size, size_t granule, bool leaf, void *cookie) { struct qcom_iommu_domain *qcom_domain = cookie; - struct iommu_fwspec *fwspec = qcom_domain->fwspec; + struct qcom_iommu_master *master = qcom_domain->master; unsigned i, reg; reg = leaf ? ARM_SMMU_CB_S1_TLBIVAL : ARM_SMMU_CB_S1_TLBIVA; - for (i = 0; i < fwspec->num_ids; i++) { - struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, fwspec->ids[i]); + for (i = 0; i < master->num_ids; i++) { + struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, master->ids[i]); size_t s = size; iova = (iova >> 12) << 12; @@ -218,14 +224,14 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain, struct device *dev) { struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain); - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); + struct qcom_iommu_master *master = dev_iommu_priv_get(dev); struct io_pgtable_ops *pgtbl_ops; struct io_pgtable_cfg pgtbl_cfg; int i, ret = 0; u32 reg; mutex_lock(&qcom_domain->init_mutex); - if (qcom_domain->iommu) + if (qcom_domain->master) goto out_unlock; pgtbl_cfg = (struct io_pgtable_cfg) { @@ -236,8 +242,7 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain, .iommu_dev = qcom_iommu->dev, }; - qcom_domain->iommu = qcom_iommu; - qcom_domain->fwspec = fwspec; + qcom_domain->master = master; pgtbl_ops = alloc_io_pgtable_ops(ARM_32_LPAE_S1, &pgtbl_cfg, qcom_domain); if (!pgtbl_ops) { @@ -251,8 +256,8 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain, domain->geometry.aperture_end = (1ULL << pgtbl_cfg.ias) - 1; domain->geometry.force_aperture = true; - for (i = 0; i < fwspec->num_ids; i++) { - struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, fwspec->ids[i]); + for (i = 0; i < master->num_ids; i++) { + struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, master->ids[i]); if (!ctx->secure_init) { ret = qcom_scm_restore_sec_cfg(qcom_iommu->sec_id, ctx->asid); @@ -316,7 +321,7 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain, return 0; out_clear_iommu: - qcom_domain->iommu = NULL; + qcom_domain->master = NULL; out_unlock: mutex_unlock(&qcom_domain->init_mutex); return ret; @@ -345,16 +350,16 @@ static void qcom_iommu_domain_free(struct iommu_domain *domain) { struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain); - if (qcom_domain->iommu) { + if (qcom_domain->master) { /* * NOTE: unmap can be called after client device is powered * off, for example, with GPUs or anything involving dma-buf. * So we cannot rely on the device_link. Make sure the IOMMU * is on to avoid unclocked accesses in the TLB inv path: */ - pm_runtime_get_sync(qcom_domain->iommu->dev); + pm_runtime_get_sync(qcom_domain->master->iommu->dev); free_io_pgtable_ops(qcom_domain->pgtbl_ops); - pm_runtime_put_sync(qcom_domain->iommu->dev); + pm_runtime_put_sync(qcom_domain->master->iommu->dev); } kfree(qcom_domain); @@ -362,7 +367,8 @@ static void qcom_iommu_domain_free(struct iommu_domain *domain) static int qcom_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) { - struct qcom_iommu_dev *qcom_iommu = dev_iommu_priv_get(dev); + struct qcom_iommu_master *master = dev_iommu_priv_get(dev); + struct qcom_iommu_dev *qcom_iommu = master->iommu; struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain); int ret; @@ -382,7 +388,7 @@ static int qcom_iommu_attach_dev(struct iommu_domain *domain, struct device *dev * Sanity check the domain. We don't support domains across * different IOMMUs. */ - if (qcom_domain->iommu != qcom_iommu) + if (qcom_domain->master->iommu != qcom_iommu) return -EINVAL; return 0; @@ -393,20 +399,20 @@ static int qcom_iommu_identity_attach(struct iommu_domain *identity_domain, { struct iommu_domain *domain = iommu_get_domain_for_dev(dev); struct qcom_iommu_domain *qcom_domain; - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); - struct qcom_iommu_dev *qcom_iommu = dev_iommu_priv_get(dev); + struct qcom_iommu_master *master = dev_iommu_priv_get(dev); + struct qcom_iommu_dev *qcom_iommu = master->iommu; unsigned int i; if (domain == identity_domain || !domain) return 0; qcom_domain = to_qcom_iommu_domain(domain); - if (WARN_ON(!qcom_domain->iommu)) + if (WARN_ON(!qcom_domain->master)) return -EINVAL; pm_runtime_get_sync(qcom_iommu->dev); - for (i = 0; i < fwspec->num_ids; i++) { - struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, fwspec->ids[i]); + for (i = 0; i < master->num_ids; i++) { + struct qcom_iommu_ctx *ctx = to_ctx(qcom_domain, master->ids[i]); /* Disable the context bank: */ iommu_writel(ctx, ARM_SMMU_CB_SCTLR, 0); @@ -461,11 +467,11 @@ static size_t qcom_iommu_unmap(struct iommu_domain *domain, unsigned long iova, * cannot rely on the device_link. Make sure the IOMMU is on to * avoid unclocked accesses in the TLB inv path: */ - pm_runtime_get_sync(qcom_domain->iommu->dev); + pm_runtime_get_sync(qcom_domain->master->iommu->dev); spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags); ret = ops->unmap_pages(ops, iova, pgsize, pgcount, gather); spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags); - pm_runtime_put_sync(qcom_domain->iommu->dev); + pm_runtime_put_sync(qcom_domain->master->iommu->dev); return ret; } @@ -478,9 +484,9 @@ static void qcom_iommu_flush_iotlb_all(struct iommu_domain *domain) if (!qcom_domain->pgtbl_ops) return; - pm_runtime_get_sync(qcom_domain->iommu->dev); + pm_runtime_get_sync(qcom_domain->master->iommu->dev); qcom_iommu_tlb_sync(pgtable->cookie); - pm_runtime_put_sync(qcom_domain->iommu->dev); + pm_runtime_put_sync(qcom_domain->master->iommu->dev); } static void qcom_iommu_iotlb_sync(struct iommu_domain *domain, @@ -523,13 +529,38 @@ static bool qcom_iommu_capable(struct device *dev, enum iommu_cap cap) } } -static struct iommu_device *qcom_iommu_probe_device(struct device *dev) +static struct iommu_device * +qcom_iommu_probe_device(struct iommu_probe_info *pinf) { - struct qcom_iommu_dev *qcom_iommu = dev_iommu_priv_get(dev); + struct qcom_iommu_dev *qcom_iommu; + struct qcom_iommu_master *master; + struct device *dev = pinf->dev; struct device_link *link; + int ret; + int i; - if (!qcom_iommu) - return ERR_PTR(-ENODEV); + qcom_iommu = iommu_of_get_single_iommu(pinf, &qcom_iommu_ops, 1, + struct qcom_iommu_dev, iommu); + if (IS_ERR(qcom_iommu)) + return ERR_CAST(qcom_iommu); + + master = iommu_fw_alloc_per_device_ids(pinf, master); + if (IS_ERR(master)) + return ERR_CAST(master); + + for (i = 0; i != master->num_ids; i++) { + u32 asid = master->ids[i]; + + /* + * Make sure the asid specified in dt is valid, so we don't have + * to sanity check this elsewhere: + */ + if (WARN_ON(asid > qcom_iommu->max_asid) || + WARN_ON(qcom_iommu->ctxs[asid] == NULL)) { + ret = -EINVAL; + goto err_free; + } + } /* * Establish the link between iommu and master, so that the @@ -540,63 +571,33 @@ static struct iommu_device *qcom_iommu_probe_device(struct device *dev) if (!link) { dev_err(qcom_iommu->dev, "Unable to create device link between %s and %s\n", dev_name(qcom_iommu->dev), dev_name(dev)); - return ERR_PTR(-ENODEV); + ret = -ENODEV; + goto err_free; } + dev_iommu_priv_set(dev, master); return &qcom_iommu->iommu; + +err_free: + kfree(master); + return ERR_PTR(ret); } -static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args) +static void qcom_iommu_release_device(struct device *dev) { - struct qcom_iommu_dev *qcom_iommu; - struct platform_device *iommu_pdev; - unsigned asid = args->args[0]; + struct qcom_iommu_master *master = dev_iommu_priv_get(dev); - if (args->args_count != 1) { - dev_err(dev, "incorrect number of iommu params found for %s " - "(found %d, expected 1)\n", - args->np->full_name, args->args_count); - return -EINVAL; - } - - iommu_pdev = of_find_device_by_node(args->np); - if (WARN_ON(!iommu_pdev)) - return -EINVAL; - - qcom_iommu = platform_get_drvdata(iommu_pdev); - - /* make sure the asid specified in dt is valid, so we don't have - * to sanity check this elsewhere: - */ - if (WARN_ON(asid > qcom_iommu->max_asid) || - WARN_ON(qcom_iommu->ctxs[asid] == NULL)) { - put_device(&iommu_pdev->dev); - return -EINVAL; - } - - if (!dev_iommu_priv_get(dev)) { - dev_iommu_priv_set(dev, qcom_iommu); - } else { - /* make sure devices iommus dt node isn't referring to - * multiple different iommu devices. Multiple context - * banks are ok, but multiple devices are not: - */ - if (WARN_ON(qcom_iommu != dev_iommu_priv_get(dev))) { - put_device(&iommu_pdev->dev); - return -EINVAL; - } - } - - return iommu_fwspec_add_ids(dev, &asid, 1); + kfree(master); } static const struct iommu_ops qcom_iommu_ops = { .identity_domain = &qcom_iommu_identity_domain, .capable = qcom_iommu_capable, .domain_alloc_paging = qcom_iommu_domain_alloc_paging, - .probe_device = qcom_iommu_probe_device, + .probe_device_pinf = qcom_iommu_probe_device, + .release_device = qcom_iommu_release_device, .device_group = generic_device_group, - .of_xlate = qcom_iommu_of_xlate, + .of_xlate = iommu_dummy_of_xlate, .pgsize_bitmap = SZ_4K | SZ_64K | SZ_1M | SZ_16M, .default_domain_ops = &(const struct iommu_domain_ops) { .attach_dev = qcom_iommu_attach_dev, -- 2.42.0