Tegra 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 tegra_smmu_of_xlate(). This now checks that all the iommu instances are the same. Tegra can self-probe without having to go through the fwspec path because it self-initializes the fwspec. Use iommu_of_allow_bus_probe() to achieve the same thing. Convert the rest of the funcs from calling dev_iommu_fwspec_get() to using the per-device data and remove all use of fwspec. Convert the places using dev_iommu_priv_get() to use the per-device data not the iommu. Signed-off-by: Jason Gunthorpe <jgg@xxxxxxxxxx> --- drivers/iommu/tegra-smmu.c | 154 +++++++++++-------------------------- 1 file changed, 47 insertions(+), 107 deletions(-) diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index cf563db3e3b48d..1daa92f524452b 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -7,6 +7,7 @@ #include <linux/debugfs.h> #include <linux/err.h> #include <linux/iommu.h> +#include <linux/iommu-driver.h> #include <linux/kernel.h> #include <linux/of.h> #include <linux/of_platform.h> @@ -19,6 +20,8 @@ #include <soc/tegra/ahb.h> #include <soc/tegra/mc.h> +static const struct iommu_ops tegra_smmu_ops; + struct tegra_smmu_group { struct list_head list; struct tegra_smmu *smmu; @@ -49,6 +52,12 @@ struct tegra_smmu { struct iommu_device iommu; /* IOMMU Core code handle */ }; +struct tegra_smmu_device { + struct tegra_smmu *smmu; + unsigned int num_ids; + u32 ids[] __counted_by(num_ids); +}; + struct tegra_smmu_as { struct iommu_domain domain; struct tegra_smmu *smmu; @@ -477,21 +486,18 @@ static void tegra_smmu_as_unprepare(struct tegra_smmu *smmu, static int tegra_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) { - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); - struct tegra_smmu *smmu = dev_iommu_priv_get(dev); + struct tegra_smmu_device *smmu_device = dev_iommu_priv_get(dev); + struct tegra_smmu *smmu = smmu_device->smmu; struct tegra_smmu_as *as = to_smmu_as(domain); unsigned int index; int err; - if (!fwspec) - return -ENOENT; - - for (index = 0; index < fwspec->num_ids; index++) { + for (index = 0; index < smmu_device->num_ids; index++) { err = tegra_smmu_as_prepare(smmu, as); if (err) goto disable; - tegra_smmu_enable(smmu, fwspec->ids[index], as->id); + tegra_smmu_enable(smmu, smmu_device->ids[index], as->id); } if (index == 0) @@ -501,7 +507,7 @@ static int tegra_smmu_attach_dev(struct iommu_domain *domain, disable: while (index--) { - tegra_smmu_disable(smmu, fwspec->ids[index], as->id); + tegra_smmu_disable(smmu, smmu_device->ids[index], as->id); tegra_smmu_as_unprepare(smmu, as); } @@ -511,22 +517,19 @@ static int tegra_smmu_attach_dev(struct iommu_domain *domain, static int tegra_smmu_identity_attach(struct iommu_domain *identity_domain, struct device *dev) { + struct tegra_smmu_device *smmu_device = dev_iommu_priv_get(dev); struct iommu_domain *domain = iommu_get_domain_for_dev(dev); - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); struct tegra_smmu_as *as; struct tegra_smmu *smmu; unsigned int index; - if (!fwspec) - return -ENODEV; - if (domain == identity_domain || !domain) return 0; as = to_smmu_as(domain); smmu = as->smmu; - for (index = 0; index < fwspec->num_ids; index++) { - tegra_smmu_disable(smmu, fwspec->ids[index], as->id); + for (index = 0; index < smmu_device->num_ids; index++) { + tegra_smmu_disable(smmu, smmu_device->ids[index], as->id); tegra_smmu_as_unprepare(smmu, as); } return 0; @@ -811,77 +814,34 @@ static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain, return SMMU_PFN_PHYS(pfn) + SMMU_OFFSET_IN_PAGE(iova); } -static struct tegra_smmu *tegra_smmu_find(struct device_node *np) +static struct iommu_device * +tegra_smmu_probe_device(struct iommu_probe_info *pinf) { - struct platform_device *pdev; - struct tegra_mc *mc; + struct tegra_smmu_device *smmu_device; + struct tegra_smmu *smmu; - pdev = of_find_device_by_node(np); - if (!pdev) - return NULL; + iommu_of_allow_bus_probe(pinf); + smmu = iommu_of_get_single_iommu(pinf, &tegra_smmu_ops, 1, + struct tegra_smmu, iommu); + if (IS_ERR(smmu)) + return ERR_CAST(smmu); - mc = platform_get_drvdata(pdev); - if (!mc) { - put_device(&pdev->dev); - return NULL; - } - - return mc->smmu; -} - -static int tegra_smmu_configure(struct tegra_smmu *smmu, struct device *dev, - struct of_phandle_args *args) -{ - const struct iommu_ops *ops = smmu->iommu.ops; - int err; - - err = iommu_fwspec_init(dev, &dev->of_node->fwnode, ops); - if (err < 0) { - dev_err(dev, "failed to initialize fwspec: %d\n", err); - return err; - } - - err = ops->of_xlate(dev, args); - if (err < 0) { - dev_err(dev, "failed to parse SW group ID: %d\n", err); - iommu_fwspec_free(dev); - return err; - } - - return 0; -} - -static struct iommu_device *tegra_smmu_probe_device(struct device *dev) -{ - struct device_node *np = dev->of_node; - struct tegra_smmu *smmu = NULL; - struct of_phandle_args args; - unsigned int index = 0; - int err; - - while (of_parse_phandle_with_args(np, "iommus", "#iommu-cells", index, - &args) == 0) { - smmu = tegra_smmu_find(args.np); - if (smmu) { - err = tegra_smmu_configure(smmu, dev, &args); - - if (err < 0) { - of_node_put(args.np); - return ERR_PTR(err); - } - } - - of_node_put(args.np); - index++; - } - - smmu = dev_iommu_priv_get(dev); - if (!smmu) - return ERR_PTR(-ENODEV); + smmu_device = iommu_fw_alloc_per_device_ids(pinf, smmu_device); + if (IS_ERR(smmu_device)) + return ERR_CAST(smmu_device); + smmu_device->smmu = smmu; + dev_iommu_priv_set(pinf->dev, smmu_device); return &smmu->iommu; } +static void tegra_smmu_release_device(struct device *dev) +{ + struct tegra_smmu_device *smmu_device = dev_iommu_priv_get(dev); + + kfree(smmu_device); +} + static const struct tegra_smmu_group_soc * tegra_smmu_find_group(struct tegra_smmu *smmu, unsigned int swgroup) { @@ -907,10 +867,10 @@ static void tegra_smmu_group_release(void *iommu_data) static struct iommu_group *tegra_smmu_device_group(struct device *dev) { - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); - struct tegra_smmu *smmu = dev_iommu_priv_get(dev); + struct tegra_smmu_device *smmu_device = dev_iommu_priv_get(dev); + struct tegra_smmu *smmu = smmu_device->smmu; const struct tegra_smmu_group_soc *soc; - unsigned int swgroup = fwspec->ids[0]; + unsigned int swgroup = smmu_device->ids[0]; struct tegra_smmu_group *group; struct iommu_group *grp; @@ -958,27 +918,6 @@ static struct iommu_group *tegra_smmu_device_group(struct device *dev) return group->group; } -static int tegra_smmu_of_xlate(struct device *dev, - struct of_phandle_args *args) -{ - struct platform_device *iommu_pdev = of_find_device_by_node(args->np); - struct tegra_mc *mc = platform_get_drvdata(iommu_pdev); - u32 id = args->args[0]; - - /* - * Note: we are here releasing the reference of &iommu_pdev->dev, which - * is mc->dev. Although some functions in tegra_smmu_ops may keep using - * its private data beyond this point, it's still safe to do so because - * the SMMU parent device is the same as the MC, so the reference count - * isn't strictly necessary. - */ - put_device(&iommu_pdev->dev); - - dev_iommu_priv_set(dev, mc->smmu); - - return iommu_fwspec_add_ids(dev, &id, 1); -} - static int tegra_smmu_def_domain_type(struct device *dev) { /* @@ -991,11 +930,11 @@ static int tegra_smmu_def_domain_type(struct device *dev) static bool tegra_smmu_get_stream_id(struct device *dev, u32 *stream_id) { - struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); + struct tegra_smmu_device *smmu_device = dev_iommu_priv_get(dev); - if (fwspec->num_ids != 1) + if (smmu_device->num_ids != 1) return false; - *stream_id = fwspec->ids[0] & 0xffff; + *stream_id = smmu_device->ids[0] & 0xffff; return true; } @@ -1003,9 +942,10 @@ static const struct iommu_ops tegra_smmu_ops = { .identity_domain = &tegra_smmu_identity_domain, .def_domain_type = &tegra_smmu_def_domain_type, .domain_alloc_paging = tegra_smmu_domain_alloc_paging, - .probe_device = tegra_smmu_probe_device, + .probe_device_pinf = tegra_smmu_probe_device, + .release_device = tegra_smmu_release_device, .device_group = tegra_smmu_device_group, - .of_xlate = tegra_smmu_of_xlate, + .of_xlate = iommu_dummy_of_xlate, .tegra_dev_iommu_get_stream_id = tegra_smmu_get_stream_id, .pgsize_bitmap = SZ_4K, .default_domain_ops = &(const struct iommu_domain_ops) { -- 2.42.0