Hi Robin,

> -----Original Message-----
> From: linux-arm-kernel [mailto:linux-arm-kernel-
> boun...@lists.infradead.org] On Behalf Of Robin Murphy
> Sent: Monday, February 29, 2016 7:16 PM
> To: iommu@lists.linux-foundation.org;
linux-arm-ker...@lists.infradead.org;
> devicet...@vger.kernel.org
> Cc: thomas.lenda...@amd.com; anup.pa...@broadcom.com;
> thunder.leiz...@huawei.com; will.dea...@arm.com;
> stuart.yo...@nxp.com; suravee.suthikulpa...@amd.com;
> tchalama...@caviumnetworks.com
> Subject: [PATCH 11/12] iommu/arm-smmu: Generic IOMMU DT bindings
> support
> 
> Implement an of_xlate callback and the appropriate registration so that we
> can configure masters via generic DT bindings. Initially, we have the
> equivalent level of functionality with respect to groups, stream ID
limits, etc.
> as for the old bindings, but the door is now open for further
improvements.
> 
> Since of_iommmu_configure() is not yet clever enough to enforce the
> necessary probe ordering dependencies, employ the same explicit device
> creation tactic as the Exynos IOMMU driver to ensure our actual SMMU
> instance is up and running in time to handle of_xlate calls.
> 
> Signed-off-by: Robin Murphy <robin.mur...@arm.com>
> ---
>  drivers/iommu/arm-smmu.c | 144
> ++++++++++++++++++++++++++++++++++-------------
>  1 file changed, 106 insertions(+), 38 deletions(-)
> 
> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index
> 91b0a1b..8fcf27a 100644
> --- a/drivers/iommu/arm-smmu.c
> +++ b/drivers/iommu/arm-smmu.c
> @@ -39,6 +39,8 @@
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/of_address.h>
> +#include <linux/of_iommu.h>
> +#include <linux/of_platform.h>
>  #include <linux/pci.h>
>  #include <linux/platform_device.h>
>  #include <linux/slab.h>
> @@ -421,6 +423,9 @@ static struct arm_smmu_master
> *find_smmu_master(struct device_node *dev_node)  {
>       struct arm_smmu_master *master;
> 
> +     if (!dev_node)
> +             return NULL;
> +
>       read_lock(&arm_smmu_masters_lock);
>       list_for_each_entry(master, &arm_smmu_masters, list)
>               if (master->of_node == dev_node)
> @@ -1011,13 +1016,6 @@ static int arm_smmu_domain_add_master(struct
> arm_smmu_domain *smmu_domain,
>       if (ret)
>               return ret;
> 
> -     /*
> -      * FIXME: This won't be needed once we have IOMMU-backed DMA
> ops
> -      * for all devices behind the SMMU.
> -      */
> -     if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
> -             return 0;
> -
>       for (i = 0; i < cfg->num_streamids; ++i) {
>               u32 idx = cfg->streamids[i].s2cr_idx - 1;
>               u32 s2cr = S2CR_TYPE_TRANS | S2CR_PRIVCFG_UNPRIV |
> @@ -1206,57 +1204,79 @@ static bool arm_smmu_capable(enum
> iommu_cap cap)
>       }
>  }
> 
> +static int arm_smmu_add_dev_streamid(struct arm_smmu_device *smmu,
> +                                  struct device *dev, u16 sid)
> +{
> +     struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
> +     int i;
> +
> +     if (!cfg) {
> +             cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
> +             if (!cfg)
> +                     return -ENOMEM;
> +
> +             cfg->smmu = smmu;
> +             dev->archdata.iommu = cfg;
> +     }
> +
> +     if (cfg->num_streamids >= MAX_MASTER_STREAMIDS)
> +             return -ENOSPC;
> +
> +     /* Avoid duplicate SIDs, as this can lead to SMR conflicts */
> +     for (i = 0; i < cfg->num_streamids; ++i)
> +             if (cfg->streamids[i].id == sid) {
> +                     dev_warn(dev, "Stream ID 0x%hx repeated;
> ignoring\n",
> +                              sid);
> +                     return 0;
> +             }
> +
> +     cfg->streamids[cfg->num_streamids++].id = sid;
> +
> +     return 0;
> +}
> +
>  static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void
> *data)  {
>       *((u16 *)data) = alias;
>       return 0; /* Continue walking */
>  }
> 
> -static int arm_smmu_init_pci_device(struct arm_smmu_device *smmu,
> -                                 struct pci_dev *pdev)
> +static int arm_smmu_init_legacy_master(struct device *dev)
>  {
> -     struct arm_smmu_master_cfg *cfg;
> +     struct arm_smmu_master *master;
> +     struct device_node *np = dev_get_dev_node(dev);
>       u16 sid;
> 
> -     cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
> -     if (!cfg)
> -             return -ENOMEM;
> -     /*
> -      * Assume Stream ID == Requester ID for now.
> -      * We need a way to describe the ID mappings in FDT.
> -      */
> -     pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
> +     master = find_smmu_master(np);
> +     if (!master)
> +             return -ENODEV;
> 
> -     cfg->streamids[0].id = sid;
> -     cfg->num_streamids = 1;
> +     if (!dev_is_pci(dev)) {
> +             dev->archdata.iommu = &master->cfg;
> +             return 0;
> +     }
> 
> -     cfg->smmu = smmu;
> -     pdev->dev.archdata.iommu = cfg;
> -
> -     return 0;
> +     /* Legacy bindings assume Stream ID == Requester ID */
> +     pci_for_each_dma_alias(to_pci_dev(dev),
> __arm_smmu_get_pci_sid, &sid);
> +     return arm_smmu_add_dev_streamid(master->cfg.smmu, dev, sid);
>  }
> 
>  static int arm_smmu_add_device(struct device *dev)  {
>       struct iommu_group *group;
> -     struct arm_smmu_master *master;
> 
> -     if (dev->archdata.iommu)
> -             return -EEXIST;
> +     if (!dev->archdata.iommu) {
> +             int ret = arm_smmu_init_legacy_master(dev);
> 
> -     master = find_smmu_master(dev_get_dev_node(dev));
> -     if (!master)
> -             return -ENODEV;
> -
> -     if (dev_is_pci(dev)) {
> -             int ret = arm_smmu_init_pci_device(master->cfg.smmu,
> -                                                to_pci_dev(dev));
>               if (ret)
>                       return ret;
> -     } else {
> -             dev->archdata.iommu = &master->cfg;
>       }
> 
> +     /*
> +      * For now, assume that the default group allocators suffice.
> +      * We might have to do some preparatory work here to properly
> +      * handle multiple devices sharing stream IDs.
> +      */
>       group = iommu_group_get_for_dev(dev);
>       if (IS_ERR(group))
>               return PTR_ERR(group);
> @@ -1268,7 +1288,7 @@ static int arm_smmu_add_device(struct device
> *dev)  static void arm_smmu_remove_device(struct device *dev)  {
>       iommu_group_remove_device(dev);
> -     if (dev_is_pci(dev))
> +     if (!find_smmu_master(dev->of_node))
>               kfree(dev->archdata.iommu);
>  }
> 
> @@ -1381,6 +1401,20 @@ out_unlock:
>       return ret;
>  }
> 
> +static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args
> +*args) {
> +     struct arm_smmu_device *smmu;
> +     struct platform_device *smmu_pdev;
> +
> +     smmu_pdev = of_find_device_by_node(args->np);
> +     if (!smmu_pdev)
> +             return -ENODEV;
> +
> +     smmu = platform_get_drvdata(smmu_pdev);
> +
> +     return arm_smmu_add_dev_streamid(smmu, dev, args->args[0]); }
> +
>  static struct iommu_ops arm_smmu_ops = {
>       .capable                = arm_smmu_capable,
>       .domain_alloc           = arm_smmu_domain_alloc,
> @@ -1395,6 +1429,7 @@ static struct iommu_ops arm_smmu_ops = {
>       .device_group           = arm_smmu_device_group,
>       .domain_get_attr        = arm_smmu_domain_get_attr,
>       .domain_set_attr        = arm_smmu_domain_set_attr,
> +     .of_xlate               = arm_smmu_of_xlate,
>       .pgsize_bitmap          = -1UL, /* Restricted during device attach
*/
>  };
> 
> @@ -1731,6 +1766,9 @@ static int arm_smmu_probe_mmu_masters(struct
> arm_smmu_device *smmu)
>       struct of_phandle_args masterspec;
>       int err, i = 0;
> 
> +     dev_notice(smmu->dev,
> +                "Deprecated \"mmu-masters\" property found; update DT
> to
> +\"iommus\" property if possible\n");
> +
>       while (!of_parse_phandle_with_args(smmu->dev->of_node, "mmu-
> masters",
>                       "#stream-id-cells", i,
>                       &masterspec)) {
> @@ -1838,7 +1876,9 @@ static int arm_smmu_device_dt_probe(struct
> platform_device *pdev)
>       }
> 
>       platform_set_drvdata(pdev, smmu);
> -     arm_smmu_probe_mmu_masters(smmu);
> +     /* Check first to avoid of_parse_phandle_with_args complaining */
> +     if (of_property_read_bool(dev->of_node, "mmu-masters"))
> +             arm_smmu_probe_mmu_masters(smmu);
>       arm_smmu_device_reset(smmu);
>       return 0;
> 
> @@ -1881,8 +1921,11 @@ static struct platform_driver arm_smmu_driver = {
> static int __init arm_smmu_init(void)  {
>       struct device_node *np;
> +     static bool done;
>       int ret;
> 
> +     if (done)
> +             return 0;
>       /*
>        * Play nice with systems that don't have an ARM SMMU by checking
> that
>        * an ARM SMMU exists in the system before proceeding with the
> driver @@ -1912,6 +1955,7 @@ static int __init arm_smmu_init(void)
>               bus_set_iommu(&pci_bus_type, &arm_smmu_ops);  #endif
> 
> +     done = true;
>       return 0;
>  }
> 
> @@ -1923,6 +1967,30 @@ static void __exit arm_smmu_exit(void)
> subsys_initcall(arm_smmu_init);  module_exit(arm_smmu_exit);
> 
> +static int __init arm_smmu_of_init(struct device_node *np) {
> +     struct arm_smmu_device *smmu;
> +     struct platform_device *pdev;
> +     int ret = arm_smmu_init();
> +
> +     if (ret)
> +             return ret;
> +
> +     pdev = of_platform_device_create(np, NULL,
> platform_bus_type.dev_root);
> +     if (!pdev)
> +             return -ENODEV;
> +
> +     smmu = platform_get_drvdata(pdev);
> +     of_iommu_set_ops(np, &arm_smmu_ops);
> +
> +     return 0;
> +}
> +IOMMU_OF_DECLARE(arm_smmuv1, "arm,smmu-v1",
> arm_smmu_of_init);
> +IOMMU_OF_DECLARE(arm_smmuv2, "arm,smmu-v2",
> arm_smmu_of_init);
> +IOMMU_OF_DECLARE(arm_mmu400, "arm,mmu-400",
> arm_smmu_of_init);
> +IOMMU_OF_DECLARE(arm_mmu401, "arm,mmu-401",
> arm_smmu_of_init);
> +IOMMU_OF_DECLARE(arm_mmu500, "arm,mmu-500",
> arm_smmu_of_init);
> +
 Thanks for this series. I am going to use and test this. Also I wanted to
 ask about the iommu probe deferral series [1] to avoid early device
registration and wanted know the direction on that ?

[1] http://lkml.iu.edu/hypermail/linux/kernel/1505.3/03280.html

Regards,
 Sricharan

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to