@@ -17,6 +17,7 @@
#include <linux/iopoll.h>
#include <linux/io-pgtable.h>
#include <linux/iommu.h>
+#include <linux/iommu-driver.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/pci.h>
@@ -38,6 +39,8 @@
#define IPMMU_UTLB_MAX 64U
+static const struct iommu_ops ipmmu_ops;
+
struct ipmmu_features {
bool use_ns_alias_offset;
bool has_cache_leaf_nodes;
@@ -67,6 +70,12 @@ struct ipmmu_vmsa_device {
struct dma_iommu_mapping *mapping;
};
+struct ipmmu_vmsa_master {
+ struct ipmmu_vmsa_device *iommu;
+ unsigned int num_ids;
+ u32 ids[] __counted_by(num_ids);
+};
+
struct ipmmu_vmsa_domain {
struct ipmmu_vmsa_device *mmu;
struct iommu_domain io_domain;
@@ -83,11 +92,6 @@ static struct ipmmu_vmsa_domain *to_vmsa_domain(struct iommu_domain *dom)
return container_of(dom, struct ipmmu_vmsa_domain, io_domain);
}
-static struct ipmmu_vmsa_device *to_ipmmu(struct device *dev)
-{
- return dev_iommu_priv_get(dev);
-}
-
#define TLB_LOOP_TIMEOUT 100 /* 100us */
/* -----------------------------------------------------------------------------
@@ -591,9 +595,9 @@ static void ipmmu_domain_free(struct iommu_domain *io_domain)
static int ipmmu_attach_device(struct iommu_domain *io_domain,
struct device *dev)
{
- struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
- struct ipmmu_vmsa_device *mmu = to_ipmmu(dev);
+ struct ipmmu_vmsa_master *master = dev_iommu_priv_get(dev);
struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain);
+ struct ipmmu_vmsa_device *mmu = master->iommu;
unsigned int i;
int ret = 0;
@@ -629,8 +633,8 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain,
if (ret < 0)
return ret;
- for (i = 0; i < fwspec->num_ids; ++i)
- ipmmu_utlb_enable(domain, fwspec->ids[i]);
+ for (i = 0; i < master->num_ids; ++i)
+ ipmmu_utlb_enable(domain, master->ids[i]);
return 0;
}
@@ -639,7 +643,7 @@ static int ipmmu_iommu_identity_attach(struct iommu_domain *identity_domain,
struct device *dev)
{
struct iommu_domain *io_domain = iommu_get_domain_for_dev(dev);
- struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ struct ipmmu_vmsa_master *master = dev_iommu_priv_get(dev);
struct ipmmu_vmsa_domain *domain;
unsigned int i;
@@ -647,8 +651,8 @@ static int ipmmu_iommu_identity_attach(struct iommu_domain *identity_domain,
return 0;
domain = to_vmsa_domain(io_domain);
- for (i = 0; i < fwspec->num_ids; ++i)
- ipmmu_utlb_disable(domain, fwspec->ids[i]);
+ for (i = 0; i < master->num_ids; ++i)
+ ipmmu_utlb_disable(domain, master->ids[i]);
/*
* TODO: Optimize by disabling the context when no device is attached.
@@ -708,20 +712,6 @@ static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain,
return domain->iop->iova_to_phys(domain->iop, iova);
}
-static int ipmmu_init_platform_device(struct device *dev,
- struct of_phandle_args *args)
-{
- struct platform_device *ipmmu_pdev;
-
- ipmmu_pdev = of_find_device_by_node(args->np);
- if (!ipmmu_pdev)
- return -ENODEV;
-
- dev_iommu_priv_set(dev, platform_get_drvdata(ipmmu_pdev));
-
- return 0;
-}
-
static const struct soc_device_attribute soc_needs_opt_in[] = {
{ .family = "R-Car Gen3", },
{ .family = "R-Car Gen4", },
@@ -772,24 +762,10 @@ static bool ipmmu_device_is_allowed(struct device *dev)
return false;
}
-static int ipmmu_of_xlate(struct device *dev,
- struct of_phandle_args *spec)
-{
- if (!ipmmu_device_is_allowed(dev))
- return -ENODEV;
-
- iommu_fwspec_add_ids(dev, spec->args, 1);
-
- /* Initialize once - xlate() will call multiple times */
- if (to_ipmmu(dev))
- return 0;
-
- return ipmmu_init_platform_device(dev, spec);
-}
-
static int ipmmu_init_arm_mapping(struct device *dev)
{
- struct ipmmu_vmsa_device *mmu = to_ipmmu(dev);
+ struct ipmmu_vmsa_master *master = dev_iommu_priv_get(dev);
+ struct ipmmu_vmsa_device *mmu = master->iommu;
int ret;
/*
@@ -831,16 +807,27 @@ static int ipmmu_init_arm_mapping(struct device *dev)
return ret;
}
-static struct iommu_device *ipmmu_probe_device(struct device *dev)
+static struct iommu_device *ipmmu_probe_device(struct iommu_probe_info *pinf)
{
- struct ipmmu_vmsa_device *mmu = to_ipmmu(dev);
+ struct device *dev = pinf->dev;
+ struct ipmmu_vmsa_master *master;
+ struct ipmmu_vmsa_device *mmu;
- /*
- * Only let through devices that have been verified in xlate()
- */
- if (!mmu)
+ if (!ipmmu_device_is_allowed(dev))
return ERR_PTR(-ENODEV);
+ mmu = iommu_of_get_single_iommu(pinf, &ipmmu_ops, -1,
+ struct ipmmu_vmsa_device, iommu);
+ if (IS_ERR(mmu))
+ return ERR_CAST(mmu);
+
+ master = iommu_fw_alloc_per_device_ids(pinf, master);
+ if (IS_ERR(master))
+ return ERR_CAST(master);
+ master->iommu = mmu;
+
+ dev_iommu_priv_set(dev, master);
+
return &mmu->iommu;
}
@@ -857,24 +844,25 @@ static void ipmmu_probe_finalize(struct device *dev)
static void ipmmu_release_device(struct device *dev)
{
- struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
- struct ipmmu_vmsa_device *mmu = to_ipmmu(dev);
+ struct ipmmu_vmsa_master *master = dev_iommu_priv_get(dev);
+ struct ipmmu_vmsa_device *mmu = master->iommu;
unsigned int i;
- for (i = 0; i < fwspec->num_ids; ++i) {
- unsigned int utlb = fwspec->ids[i];
+ for (i = 0; i < master->num_ids; ++i) {
+ unsigned int utlb = master->ids[i];
ipmmu_imuctr_write(mmu, utlb, 0);
mmu->utlb_ctx[utlb] = IPMMU_CTX_INVALID;
}
arm_iommu_release_mapping(mmu->mapping);
+ kfree(master);
}
static const struct iommu_ops ipmmu_ops = {
.identity_domain = &ipmmu_iommu_identity_domain,
.domain_alloc_paging = ipmmu_domain_alloc_paging,
- .probe_device = ipmmu_probe_device,
+ .probe_device_pinf = ipmmu_probe_device,
.release_device = ipmmu_release_device,
.probe_finalize = ipmmu_probe_finalize,
/*
@@ -884,7 +872,7 @@ static const struct iommu_ops ipmmu_ops = {
.device_group = IS_ENABLED(CONFIG_ARM) && !IS_ENABLED(CONFIG_IOMMU_DMA)
? generic_device_group : generic_single_device_group,
.pgsize_bitmap = SZ_1G | SZ_2M | SZ_4K,
- .of_xlate = ipmmu_of_xlate,
+ .of_xlate = iommu_dummy_of_xlate,
.default_domain_ops = &(const struct iommu_domain_ops) {
.attach_dev = ipmmu_attach_device,
.map_pages = ipmmu_map,
ipmmmu-vmsa supports a single instance with multiple ids. It has a special check if the device is permitted, move that to the top of probe. Use iommu_of_get_single_iommu() to check and obtain the iommu device. Introduce a per-device data to store the iommu and ids list. Allocate and initialize it with iommu_fw_alloc_per_device_ids(). Remove ipmmu_of_xlate(), ipmmu_init_platform_device(), and to_ipmmu(). 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@nvidia.com> --- drivers/iommu/ipmmu-vmsa.c | 96 +++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 54 deletions(-)