Hi Joerg, Today's linux-next merge of the iommu tree got a conflict in drivers/iommu/tegra-smmu.c between commit a2257374a4bd ("memory: Add NVIDIA Tegra memory controller support") from the tegra tree and commit 315786ebbf4a ("iommu: Add iommu_map_sg() function") from the iommu tree. I fixed it up (see below) and can carry the fix as necessary (no action is required). -- Cheers, Stephen Rothwell sfr@xxxxxxxxxxxxxxxx diff --cc drivers/iommu/tegra-smmu.c index 0909e0bae9ec,73e845a66925..000000000000 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@@ -535,187 -1049,248 +535,188 @@@ static int tegra_smmu_map(struct iommu_ return 0; } -static int smmu_debugfs_stats_open(struct inode *inode, struct file *file) +static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova, + size_t size) { - return single_open(file, smmu_debugfs_stats_show, inode->i_private); -} + struct tegra_smmu_as *as = domain->priv; + struct tegra_smmu *smmu = as->smmu; + unsigned long offset; + struct page *page; + u32 *pte; -static const struct file_operations smmu_debugfs_stats_fops = { - .open = smmu_debugfs_stats_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = smmu_debugfs_stats_write, -}; + pte = as_get_pte(as, iova, &page); + if (!pte) + return 0; -static void smmu_debugfs_delete(struct smmu_device *smmu) -{ - debugfs_remove_recursive(smmu->debugfs_root); - kfree(smmu->debugfs_info); + offset = offset_in_page(pte); + as_put_pte(as, iova); + + smmu->soc->ops->flush_dcache(page, offset, 4); + smmu_flush_ptc(smmu, page, offset); + smmu_flush_tlb_group(smmu, as->id, iova); + smmu_flush(smmu); + + return size; } -static void smmu_debugfs_create(struct smmu_device *smmu) +static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain, + dma_addr_t iova) { - int i; - size_t bytes; - struct dentry *root; - - bytes = ARRAY_SIZE(smmu_debugfs_mc) * ARRAY_SIZE(smmu_debugfs_cache) * - sizeof(*smmu->debugfs_info); - smmu->debugfs_info = kmalloc(bytes, GFP_KERNEL); - if (!smmu->debugfs_info) - return; - - root = debugfs_create_dir(dev_name(smmu->dev), NULL); - if (!root) - goto err_out; - smmu->debugfs_root = root; - - for (i = 0; i < ARRAY_SIZE(smmu_debugfs_mc); i++) { - int j; - struct dentry *mc; - - mc = debugfs_create_dir(smmu_debugfs_mc[i], root); - if (!mc) - goto err_out; - - for (j = 0; j < ARRAY_SIZE(smmu_debugfs_cache); j++) { - struct dentry *cache; - struct smmu_debugfs_info *info; - - info = smmu->debugfs_info; - info += i * ARRAY_SIZE(smmu_debugfs_mc) + j; - info->smmu = smmu; - info->mc = i; - info->cache = j; - - cache = debugfs_create_file(smmu_debugfs_cache[j], - S_IWUGO | S_IRUGO, mc, - (void *)info, - &smmu_debugfs_stats_fops); - if (!cache) - goto err_out; - } - } + struct tegra_smmu_as *as = domain->priv; + struct page *page; + unsigned long pfn; + u32 *pte; - return; + pte = as_get_pte(as, iova, &page); + pfn = *pte & SMMU_PFN_MASK; -err_out: - smmu_debugfs_delete(smmu); + return PFN_PHYS(pfn); } -static int tegra_smmu_suspend(struct device *dev) +static struct tegra_smmu *tegra_smmu_find(struct device_node *np) { - struct smmu_device *smmu = dev_get_drvdata(dev); + struct platform_device *pdev; + struct tegra_mc *mc; - smmu->translation_enable_0 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_0); - smmu->translation_enable_1 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_1); - smmu->translation_enable_2 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_2); - smmu->asid_security = smmu_read(smmu, SMMU_ASID_SECURITY); - return 0; + pdev = of_find_device_by_node(np); + if (!pdev) + return NULL; + + mc = platform_get_drvdata(pdev); + if (!mc) + return NULL; + + return mc->smmu; } -static int tegra_smmu_resume(struct device *dev) +static int tegra_smmu_add_device(struct device *dev) { - struct smmu_device *smmu = dev_get_drvdata(dev); - unsigned long flags; - int err; + struct device_node *np = dev->of_node; + struct of_phandle_args args; + unsigned int index = 0; - spin_lock_irqsave(&smmu->lock, flags); - err = smmu_setup_regs(smmu); - spin_unlock_irqrestore(&smmu->lock, flags); - return err; + while (of_parse_phandle_with_args(np, "iommus", "#iommu-cells", index, + &args) == 0) { + struct tegra_smmu *smmu; + + smmu = tegra_smmu_find(args.np); + if (smmu) { + /* + * Only a single IOMMU master interface is currently + * supported by the Linux kernel, so abort after the + * first match. + */ + dev->archdata.iommu = smmu; + break; + } + + index++; + } + + return 0; } -static int tegra_smmu_probe(struct platform_device *pdev) +static void tegra_smmu_remove_device(struct device *dev) { - struct smmu_device *smmu; - struct device *dev = &pdev->dev; - int i, asids, err = 0; - dma_addr_t uninitialized_var(base); - size_t bytes, uninitialized_var(size); + dev->archdata.iommu = NULL; +} - if (smmu_handle) - return -EIO; +static const struct iommu_ops tegra_smmu_ops = { + .capable = tegra_smmu_capable, + .domain_init = tegra_smmu_domain_init, + .domain_destroy = tegra_smmu_domain_destroy, + .attach_dev = tegra_smmu_attach_dev, + .detach_dev = tegra_smmu_detach_dev, + .add_device = tegra_smmu_add_device, + .remove_device = tegra_smmu_remove_device, + .map = tegra_smmu_map, + .unmap = tegra_smmu_unmap, ++ .map_sg = default_iommu_map_sg, + .iova_to_phys = tegra_smmu_iova_to_phys, - BUILD_BUG_ON(PAGE_SHIFT != SMMU_PAGE_SHIFT); + .pgsize_bitmap = SZ_4K, +}; - if (of_property_read_u32(dev->of_node, "nvidia,#asids", &asids)) - return -ENODEV; +static void tegra_smmu_ahb_enable(void) +{ + static const struct of_device_id ahb_match[] = { + { .compatible = "nvidia,tegra30-ahb", }, + { } + }; + struct device_node *ahb; - bytes = sizeof(*smmu) + asids * sizeof(*smmu->as); - smmu = devm_kzalloc(dev, bytes, GFP_KERNEL); - if (!smmu) { - dev_err(dev, "failed to allocate smmu_device\n"); - return -ENOMEM; + ahb = of_find_matching_node(NULL, ahb_match); + if (ahb) { + tegra_ahb_enable_smmu(ahb); + of_node_put(ahb); } +} - smmu->nregs = pdev->num_resources; - smmu->regs = devm_kzalloc(dev, 2 * smmu->nregs * sizeof(*smmu->regs), - GFP_KERNEL); - smmu->rege = smmu->regs + smmu->nregs; - if (!smmu->regs) - return -ENOMEM; - for (i = 0; i < smmu->nregs; i++) { - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - smmu->regs[i] = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(smmu->regs[i])) - return PTR_ERR(smmu->regs[i]); - smmu->rege[i] = smmu->regs[i] + resource_size(res) - 1; - } - /* Same as "mc" 1st regiter block start address */ - smmu->regbase = (void __iomem *)((u32)smmu->regs[0] & PAGE_MASK); +struct tegra_smmu *tegra_smmu_probe(struct device *dev, + const struct tegra_smmu_soc *soc, + struct tegra_mc *mc) +{ + struct tegra_smmu *smmu; + size_t size; + u32 value; + int err; - err = of_get_dma_window(dev->of_node, NULL, 0, NULL, &base, &size); - if (err) - return -ENODEV; + /* This can happen on Tegra20 which doesn't have an SMMU */ + if (!soc) + return NULL; - if (size & SMMU_PAGE_MASK) - return -EINVAL; + smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); + if (!smmu) + return ERR_PTR(-ENOMEM); - size >>= SMMU_PAGE_SHIFT; - if (!size) - return -EINVAL; + /* + * This is a bit of a hack. Ideally we'd want to simply return this + * value. However the IOMMU registration process will attempt to add + * all devices to the IOMMU when bus_set_iommu() is called. In order + * not to rely on global variables to track the IOMMU instance, we + * set it here so that it can be looked up from the .add_device() + * callback via the IOMMU device's .drvdata field. + */ + mc->smmu = smmu; - smmu->ahb = of_parse_phandle(dev->of_node, "nvidia,ahb", 0); - if (!smmu->ahb) - return -ENODEV; + size = BITS_TO_LONGS(soc->num_asids) * sizeof(long); - smmu->dev = dev; - smmu->num_as = asids; - smmu->iovmm_base = base; - smmu->page_count = size; - - smmu->translation_enable_0 = ~0; - smmu->translation_enable_1 = ~0; - smmu->translation_enable_2 = ~0; - smmu->asid_security = 0; - - for (i = 0; i < smmu->num_as; i++) { - struct smmu_as *as = &smmu->as[i]; - - as->smmu = smmu; - as->asid = i; - as->pdir_attr = _PDIR_ATTR; - as->pde_attr = _PDE_ATTR; - as->pte_attr = _PTE_ATTR; - - spin_lock_init(&as->lock); - spin_lock_init(&as->client_lock); - INIT_LIST_HEAD(&as->client); - } - spin_lock_init(&smmu->lock); - err = smmu_setup_regs(smmu); - if (err) - return err; - platform_set_drvdata(pdev, smmu); + smmu->asids = devm_kzalloc(dev, size, GFP_KERNEL); + if (!smmu->asids) + return ERR_PTR(-ENOMEM); - smmu->avp_vector_page = alloc_page(GFP_KERNEL); - if (!smmu->avp_vector_page) - return -ENOMEM; + mutex_init(&smmu->lock); - smmu_debugfs_create(smmu); - smmu_handle = smmu; - bus_set_iommu(&platform_bus_type, &smmu_iommu_ops); - return 0; -} + smmu->regs = mc->regs; + smmu->soc = soc; + smmu->dev = dev; + smmu->mc = mc; -static int tegra_smmu_remove(struct platform_device *pdev) -{ - struct smmu_device *smmu = platform_get_drvdata(pdev); - int i; + value = SMMU_PTC_CONFIG_ENABLE | SMMU_PTC_CONFIG_INDEX_MAP(0x3f); - smmu_debugfs_delete(smmu); + if (soc->supports_request_limit) + value |= SMMU_PTC_CONFIG_REQ_LIMIT(8); - smmu_write(smmu, SMMU_CONFIG_DISABLE, SMMU_CONFIG); - for (i = 0; i < smmu->num_as; i++) - free_pdir(&smmu->as[i]); - __free_page(smmu->avp_vector_page); - smmu_handle = NULL; - return 0; -} + smmu_writel(smmu, value, SMMU_PTC_CONFIG); -static const struct dev_pm_ops tegra_smmu_pm_ops = { - .suspend = tegra_smmu_suspend, - .resume = tegra_smmu_resume, -}; + value = SMMU_TLB_CONFIG_HIT_UNDER_MISS | + SMMU_TLB_CONFIG_ACTIVE_LINES(0x20); -static const struct of_device_id tegra_smmu_of_match[] = { - { .compatible = "nvidia,tegra30-smmu", }, - { }, -}; -MODULE_DEVICE_TABLE(of, tegra_smmu_of_match); - -static struct platform_driver tegra_smmu_driver = { - .probe = tegra_smmu_probe, - .remove = tegra_smmu_remove, - .driver = { - .owner = THIS_MODULE, - .name = "tegra-smmu", - .pm = &tegra_smmu_pm_ops, - .of_match_table = tegra_smmu_of_match, - }, -}; + if (soc->supports_round_robin_arbitration) + value |= SMMU_TLB_CONFIG_ROUND_ROBIN_ARBITRATION; -static int tegra_smmu_init(void) -{ - return platform_driver_register(&tegra_smmu_driver); -} + smmu_writel(smmu, value, SMMU_TLB_CONFIG); -static void __exit tegra_smmu_exit(void) -{ - platform_driver_unregister(&tegra_smmu_driver); -} + smmu_flush_ptc(smmu, NULL, 0); + smmu_flush_tlb(smmu); + smmu_writel(smmu, SMMU_CONFIG_ENABLE, SMMU_CONFIG); + smmu_flush(smmu); + + tegra_smmu_ahb_enable(); -subsys_initcall(tegra_smmu_init); -module_exit(tegra_smmu_exit); + err = bus_set_iommu(&platform_bus_type, &tegra_smmu_ops); + if (err < 0) + return ERR_PTR(err); -MODULE_DESCRIPTION("IOMMU API for SMMU in Tegra30"); -MODULE_AUTHOR("Hiroshi DOYU <hdoyu@xxxxxxxxxx>"); -MODULE_ALIAS("platform:tegra-smmu"); -MODULE_LICENSE("GPL v2"); + return smmu; +}
Attachment:
pgp6kJ8kwG4O8.pgp
Description: OpenPGP digital signature