Follow arm,smmu's "mmu-masters" binding. Signed-off-by: Hiroshi Doyu <hdoyu@xxxxxxxxxx> --- Update: Newly added for v4. In v3, I used "nvidia,swgroups" and "nvidia,memory-clients" bindings. --- .../bindings/iommu/nvidia,tegra30-smmu.txt | 28 ++++- drivers/iommu/tegra-smmu.c | 138 +++++++++++++++++---- 2 files changed, 141 insertions(+), 25 deletions(-) diff --git a/Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt b/Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt index 89fb543..51884e9 100644 --- a/Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt +++ b/Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt @@ -8,9 +8,16 @@ Required properties: - nvidia,#asids : # of ASIDs - dma-window : IOVA start address and length. - nvidia,ahb : phandle to the ahb bus connected to SMMU. +- mmu-masters : A list of phandles to device nodes representing bus + masters for which the SMMU can provide a translation + and their corresponding StreamIDs (see example below). + Each device node linked from this list must have a + "#stream-id-cells" property, indicating the number of + StreamIDs(swgroup ID) associated with it, which is defined + in "include/dt-bindings/memory/tegra-swgroup.h". Example: - smmu { + iommu { compatible = "nvidia,tegra30-smmu"; reg = <0x7000f010 0x02c 0x7000f1f0 0x010 @@ -18,4 +25,23 @@ Example: nvidia,#asids = <4>; /* # of ASIDs */ dma-window = <0 0x40000000>; /* IOVA start & length */ nvidia,ahb = <&ahb>; + + mmu-masters = <&host1x TEGRA_SWGROUP_HC>, + <&mpe TEGRA_SWGROUP_MPE>, + <&vi TEGRA_SWGROUP_VI>, + <&epp TEGRA_SWGROUP_EPP>, + <&isp TEGRA_SWGROUP_ISP>, + <&gr2d TEGRA_SWGROUP_G2>, + <&gr3d TEGRA_SWGROUP_NV TEGRA_SWGROUP_NV2>, + <&dc TEGRA_SWGROUP_DC>, + <&dcb TEGRA_SWGROUP_DCB>, + <&uarta TEGRA_SWGROUP_PPCS>, + <&uartb TEGRA_SWGROUP_PPCS>, + <&uartc TEGRA_SWGROUP_PPCS>, + <&uartd TEGRA_SWGROUP_PPCS>, + <&uarte TEGRA_SWGROUP_PPCS>, + <&sdhci0 TEGRA_SWGROUP_PPCS>, + <&sdhci1 TEGRA_SWGROUP_PPCS>, + <&sdhci2 TEGRA_SWGROUP_PPCS>, + <&sdhci3 TEGRA_SWGROUP_PPCS>; }; diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 67252e1..ab198ce 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -206,10 +206,12 @@ enum { * Per client for address space */ struct smmu_client { + struct device_node *of_node; + struct rb_node node; struct device *dev; struct list_head list; struct smmu_as *as; - u32 hwgrp; + u64 hwgrp; }; /* @@ -249,6 +251,7 @@ struct smmu_device { spinlock_t lock; char *name; struct device *dev; + struct rb_root clients; struct page *avp_vector_page; /* dummy page shared by all AS's */ /* @@ -326,23 +329,22 @@ static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs) */ #define FLUSH_SMMU_REGS(smmu) smmu_read(smmu, SMMU_CONFIG) -#define smmu_client_hwgrp(c) (u32)((c)->dev->platform_data) - static int __smmu_client_set_hwgrp(struct smmu_client *c, - unsigned long map, int on) + u64 map, int on) { int i; struct smmu_as *as = c->as; u32 val, offs, mask = SMMU_ASID_ENABLE(as->asid); struct smmu_device *smmu = as->smmu; + unsigned long *bitmap = (unsigned long *)↦ WARN_ON(!on && map); if (on && !map) return -EINVAL; if (!on) - map = smmu_client_hwgrp(c); + map = c->hwgrp; - for_each_set_bit(i, &map, HWGRP_COUNT) { + for_each_set_bit(i, bitmap, TEGRA_SWGROUP_MAX) { offs = HWGRP_ASID_REG(i); val = smmu_read(smmu, offs); if (on) { @@ -360,7 +362,7 @@ static int __smmu_client_set_hwgrp(struct smmu_client *c, return 0; err_hw_busy: - for_each_set_bit(i, &map, HWGRP_COUNT) { + for_each_set_bit(i, bitmap, TEGRA_SWGROUP_MAX) { offs = HWGRP_ASID_REG(i); val = smmu_read(smmu, offs); val &= ~mask; @@ -732,27 +734,26 @@ static int smmu_iommu_domain_has_cap(struct iommu_domain *domain, return 0; } +static struct smmu_client *find_smmu_client(struct smmu_device *smmu, + struct device_node *dev_node); + static int smmu_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) { struct smmu_as *as = domain->priv; struct smmu_device *smmu = as->smmu; struct smmu_client *client, *c; - u32 map; int err; - client = devm_kzalloc(smmu->dev, sizeof(*c), GFP_KERNEL); + client = find_smmu_client(smmu, dev->of_node); if (!client) - return -ENOMEM; - client->dev = dev; - client->as = as; - map = (unsigned long)dev->platform_data; - if (!map) return -EINVAL; - err = smmu_client_enable_hwgrp(client, map); + client->dev = dev; + client->as = as; + err = smmu_client_enable_hwgrp(client, client->hwgrp); if (err) - goto err_hwgrp; + return -EINVAL; spin_lock(&as->client_lock); list_for_each_entry(c, &as->client, list) { @@ -770,7 +771,7 @@ static int smmu_iommu_attach_dev(struct iommu_domain *domain, * Reserve "page zero" for AVP vectors using a common dummy * page. */ - if (map & TEGRA_SWGROUP_BIT(AVPC)) { + if (client->hwgrp & TEGRA_SWGROUP_BIT(AVPC)) { struct page *page; page = as->smmu->avp_vector_page; @@ -785,8 +786,6 @@ static int smmu_iommu_attach_dev(struct iommu_domain *domain, err_client: smmu_client_disable_hwgrp(client); spin_unlock(&as->client_lock); -err_hwgrp: - devm_kfree(smmu->dev, client); return err; } @@ -803,7 +802,6 @@ static void smmu_iommu_detach_dev(struct iommu_domain *domain, if (c->dev == dev) { smmu_client_disable_hwgrp(c); list_del(&c->list); - devm_kfree(smmu->dev, c); c->as = NULL; dev_dbg(smmu->dev, "%s is detached\n", dev_name(c->dev)); @@ -907,11 +905,14 @@ enum { static int smmu_iommu_add_device(struct device *dev) { int err = -EPROBE_DEFER; - u64 swgroups; struct dma_iommu_mapping *map = NULL; + struct smmu_client *client; + + client = find_smmu_client(smmu_handle, dev->of_node); + if (!client) + return -EINVAL; - swgroups = smmu_of_get_memory_client(dev); - switch (swgroups) { + switch (client->hwgrp) { case TEGRA_SWGROUP_BIT(PPCS): map = smmu_handle->map[SYSTEM_PROTECTED]; break; @@ -924,7 +925,7 @@ static int smmu_iommu_add_device(struct device *dev) err = arm_iommu_attach_device(dev, map); pr_debug("swgroups=%016llx map=%p err=%d %s\n", - swgroups, map, err, dev_name(dev)); + client->hwgrp, map, err, dev_name(dev)); return err; } @@ -1151,6 +1152,77 @@ static void tegra_smmu_create_default_map(struct smmu_device *smmu) } } +static struct smmu_client *find_smmu_client(struct smmu_device *smmu, + struct device_node *dev_node) +{ + struct rb_node *node = smmu->clients.rb_node; + + while (node) { + struct smmu_client *client; + + client = container_of(node, struct smmu_client, node); + if (dev_node < client->of_node) + node = node->rb_left; + else if (dev_node > client->of_node) + node = node->rb_right; + else + return client; + } + + return NULL; +} + +static int insert_smmu_client(struct smmu_device *smmu, + struct smmu_client *client) +{ + struct rb_node **new, *parent; + + new = &smmu->clients.rb_node; + parent = NULL; + while (*new) { + struct smmu_client *this; + this = container_of(*new, struct smmu_client, node); + + parent = *new; + if (client->of_node < this->of_node) + new = &((*new)->rb_left); + else if (client->of_node > this->of_node) + new = &((*new)->rb_right); + else + return -EEXIST; + } + + rb_link_node(&client->node, parent, new); + rb_insert_color(&client->node, &smmu->clients); + return 0; +} + +static int register_smmu_client(struct smmu_device *smmu, + struct device *dev, + struct of_phandle_args *args) +{ + struct smmu_client *client; + int i; + + client = find_smmu_client(smmu, args->np); + if (client) { + dev_err(dev, + "rejecting multiple registrations for client device %s\n", + args->np->full_name); + return -EBUSY; + } + + client = devm_kzalloc(dev, sizeof(*client), GFP_KERNEL); + if (!client) + return -ENOMEM; + + client->of_node = args->np; + for (i = 0; i < args->args_count; i++) + client->hwgrp |= 1ULL << args->args[i]; + + return insert_smmu_client(smmu, client); +} + static int tegra_smmu_probe(struct platform_device *pdev) { struct smmu_device *smmu; @@ -1158,6 +1230,7 @@ static int tegra_smmu_probe(struct platform_device *pdev) int i, asids, err = 0; dma_addr_t uninitialized_var(base); size_t bytes, uninitialized_var(size); + struct of_phandle_args args; if (smmu_handle) return -EIO; @@ -1238,6 +1311,23 @@ static int tegra_smmu_probe(struct platform_device *pdev) return err; platform_set_drvdata(pdev, smmu); + i = 0; + smmu->clients = RB_ROOT; + while (true) { + err = of_parse_phandle_with_args(dev->of_node, "mmu-masters", + "#stream-id-cells", i, &args); + if (err) + break; + + err = register_smmu_client(smmu, dev, &args); + if (err) { + dev_err(dev, "failed to add client %s\n", + args.np->full_name); + } + + i++; + } + smmu->avp_vector_page = alloc_page(GFP_KERNEL); if (!smmu->avp_vector_page) return -ENOMEM; -- 1.8.1.5 -- To unsubscribe from this list: send the line "unsubscribe linux-tegra" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html