Currently the ARM SMMU driver only considers the bus master devices in the device tree. The master device and stream ID information is maintained per SMMU. Currently there is no mechanism for representing this information in case of PCI or hot plugged devices.
This RFC patch proposes a mechanism for representing this information for hot plugged/PCI devices. Patch doesn't contain the add_device callback modification for hot plug devices. This would be bus specific and would be responsible for populating the hot plug devices masters list for the SMMU. Signed-off-by: Varun Sethi <varun.se...@freescale.com> --- drivers/iommu/arm-smmu.c | 93 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 1d9ab39..6c10df4 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -327,6 +327,9 @@ struct arm_smmu_master { * SMMU chain. */ struct rb_node node; + /* Following fields correspond to the hot plug masters */ + struct list_head hotplug_masters_node; + struct device *dev; int num_streamids; u16 streamids[MAX_MASTER_STREAMIDS]; @@ -371,6 +374,12 @@ struct arm_smmu_device { struct list_head list; struct rb_root masters; + /* + * Hot plug master list and lock to + * protect the list. + */ + struct list_head hotplug_masters_list; + spinlock_t lock; }; struct arm_smmu_cfg { @@ -401,6 +410,31 @@ struct arm_smmu_domain { static DEFINE_SPINLOCK(arm_smmu_devices_lock); static LIST_HEAD(arm_smmu_devices); +static int is_device_hotplug(struct device *dev) +{ + return (dev->bus != &platform_bus_type) && + (dev->bus != &amba_bustype); +} + +static struct arm_smmu_master *find_smmu_hotplug_master(struct arm_smmu_device *smmu, + struct device *dev) +{ + struct arm_smmu_master *master; + unsigned long flags; + bool found = 0; + + spin_lock_irqsave(&smmu->lock, flags); + list_for_each_entry(master, &smmu->hotplug_masters_list, + hotplug_masters_node) + if (master->dev == dev) { + found = 1; + break; + } + spin_unlock_irqrestore(&smmu->lock, flags); + + return found ? master : NULL; +} + static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu, struct device_node *dev_node) { @@ -421,6 +455,36 @@ static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu, return NULL; } +static int remove_smmu_hotplug_master(struct arm_smmu_device *smmu, + struct device *dev) +{ + unsigned long flags; + struct arm_smmu_master *master, *tmp; + bool found = 0; + + spin_lock_irqsave(&smmu->lock, flags); + list_for_each_entry_safe(master, tmp, &smmu->hotplug_masters_list, + hotplug_masters_node) + if (master->dev == dev) { + found = 1; + list_del(&master->hotplug_masters_node); + break; + } + spin_unlock_irqrestore(&smmu->lock, flags); + + return found ? 0 : -ENODEV; +} + +static void insert_smmu_hotplug_master(struct arm_smmu_device *smmu, + struct arm_smmu_master *master) +{ + unsigned long flags; + + spin_lock_irqsave(&smmu->lock, flags); + list_add(&master->hotplug_masters_node, &smmu->hotplug_masters_list); + spin_unlock_irqrestore(&smmu->lock, flags); +} + static int insert_smmu_master(struct arm_smmu_device *smmu, struct arm_smmu_master *master) { @@ -816,6 +880,15 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain) writel_relaxed(reg, cb_base + ARM_SMMU_CB_SCTLR); } +static struct arm_smmu_master *get_smmu_master(struct arm_smmu_device *smmu, + struct device *dev) +{ + if (is_device_hotplug(dev)) + return find_smmu_hotplug_master(smmu, dev); + else + return find_smmu_master(smmu, dev->of_node); +} + static int arm_smmu_init_domain_context(struct iommu_domain *domain, struct device *dev) { @@ -837,7 +910,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, smmu_domain->output_mask &= (1ULL << smmu->s2_output_size) - 1; } while ((parent = find_parent_smmu(smmu))); - if (!find_smmu_master(smmu, dev->of_node)) { + if (!get_smmu_master(smmu, dev)) { dev_err(dev, "unable to find root SMMU for device\n"); return -ENODEV; } @@ -1188,7 +1261,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) spin_unlock_irqrestore(&smmu_domain->lock, flags); /* Looks ok, so add the device to the domain */ - master = find_smmu_master(smmu_domain->leaf_smmu, dev->of_node); + master = get_smmu_master(smmu_domain->leaf_smmu, dev); if (!master) return -ENODEV; @@ -1204,7 +1277,7 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev) struct arm_smmu_domain *smmu_domain = domain->priv; struct arm_smmu_master *master; - master = find_smmu_master(smmu_domain->leaf_smmu, dev->of_node); + master = get_smmu_master(smmu_domain->leaf_smmu, dev); if (master) arm_smmu_domain_remove_master(smmu_domain, master); } @@ -1570,6 +1643,15 @@ static int arm_smmu_add_device(struct device *dev) static void arm_smmu_remove_device(struct device *dev) { + if (is_device_hotplug(dev)) { + int ret; + struct arm_smmu_device *smmu = dev->archdata.iommu; + + ret = remove_smmu_hotplug_master(smmu, dev); + WARN_ON(ret); + iommu_group_remove_device(dev); + } + dev->archdata.iommu = NULL; iommu_group_remove_device(dev); } @@ -1864,6 +1946,11 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) i = 0; smmu->masters = RB_ROOT; + + /* Intialize the hot plug masters list here */ + INIT_LIST_HEAD(&smmu->hotplug_masters_list); + spin_lock_init(&smmu->lock); + while (!of_parse_phandle_with_args(dev->of_node, "mmu-masters", "#stream-id-cells", i, &masterspec)) { -- 1.7.9.5 _______________________________________________ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu