diff options
author | Ryo Kataoka <ryo.kataoka.wt@renesas.com> | 2019-03-22 20:50:35 +0900 |
---|---|---|
committer | Ryo Kataoka <ryo.kataoka.wt@renesas.com> | 2019-03-22 20:50:35 +0900 |
commit | 538d41278caaf9b4066db035a11c4ef38f5912f3 (patch) | |
tree | 73ef298816a80a950bb49fda51145d7e0ecd0a28 | |
parent | 442e4daf2ba7cee3749f0cc6c6f0b3d15f0804ed (diff) | |
parent | e2acafb18264fe3dd3c69e209ac96ab889277555 (diff) | |
download | renesas-bsp-538d41278caaf9b4066db035a11c4ef38f5912f3.tar.gz |
Merge branch 'rcar-3.9.2/iommu-ipmmu-vmsa.rc3' into v4.14.75-ltsi/rcar-3.9.3
* rcar-3.9.2/iommu-ipmmu-vmsa.rc3:
driver core: Postpone DMA tear-down until after devres release
iommu/ipmmu-vmsa: Fix memory leak when adding master device
iommu/ipmmu-vmsa: Allow PCI Host controller to be a proxy for all connected PCI devices
iommu/ipmmu-vmsa: Bypass whitelist check for unsupported SoCs
iommu/ipmmu-vmsa: Support suspend/resume by default
-rw-r--r-- | drivers/base/dd.c | 2 | ||||
-rw-r--r-- | drivers/iommu/ipmmu-vmsa.c | 143 |
2 files changed, 79 insertions, 66 deletions
diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 55fc31f6fe7f9..ce6352f769993 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -853,9 +853,9 @@ static void __device_release_driver(struct device *dev, struct device *parent) drv->remove(dev); device_links_driver_cleanup(dev); - dma_deconfigure(dev); devres_release_all(dev); + dma_deconfigure(dev); dev->driver = NULL; dev_set_drvdata(dev, NULL); if (dev->pm_domain && dev->pm_domain->dismiss) diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 6da3182e24838..e1aabb7295a40 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -1,7 +1,7 @@ /* * IPMMU VMSA * - * Copyright (C) 2014 Renesas Electronics Corporation + * Copyright (C) 2014-2019 Renesas Electronics Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ #include <linux/sizes.h> #include <linux/slab.h> #include <linux/sys_soc.h> +#include <linux/pci.h> #if defined(CONFIG_ARM) && !defined(CONFIG_IOMMU_DMA) #include <asm/dma-iommu.h> @@ -57,13 +58,11 @@ struct ipmmu_features { #endif }; -#ifdef CONFIG_RCAR_DDR_BACKUP struct hw_register { char *reg_name; unsigned int reg_offset; unsigned int reg_data; }; -#endif struct ipmmu_vmsa_device { struct device *dev; @@ -76,11 +75,7 @@ struct ipmmu_vmsa_device { spinlock_t lock; /* Protects ctx and domains[] */ DECLARE_BITMAP(ctx, IPMMU_CTX_MAX); struct ipmmu_vmsa_domain *domains[IPMMU_CTX_MAX]; -#ifdef CONFIG_RCAR_DDR_BACKUP struct hw_register *reg_backup[IPMMU_CTX_MAX]; - unsigned int *utlbs_val; - unsigned int *asids_val; -#endif struct iommu_group *group; struct dma_iommu_mapping *mapping; @@ -100,15 +95,15 @@ struct ipmmu_vmsa_domain { struct mutex mutex; /* Protects mappings */ }; -#ifdef CONFIG_RCAR_DDR_BACKUP struct ipmmu_vmsa_backup { struct device *dev; + unsigned int *utlbs_val; + unsigned int *asids_val; struct list_head list; }; static DEFINE_SPINLOCK(ipmmu_devices_backup_lock); static LIST_HEAD(ipmmu_devices_backup); -#endif static struct ipmmu_vmsa_domain *to_vmsa_domain(struct iommu_domain *dom) { @@ -422,7 +417,6 @@ static struct ipmmu_whitelist *r8a77990_whitelist[] = { #define IMUASID_ASID0_MASK (0xff << 0) #define IMUASID_ASID0_SHIFT 0 -#ifdef CONFIG_RCAR_DDR_BACKUP #define HW_REGISTER_BACKUP_SIZE ARRAY_SIZE(root_pgtable0_reg) static struct hw_register root_pgtable0_reg[] = { {"IMTTLBR0", IMTTLBR0, 0}, @@ -522,7 +516,6 @@ static struct hw_register *root_pgtable[IPMMU_CTX_MAX] = { root_pgtable6_reg, root_pgtable7_reg, }; -#endif /* CONFIG_RCAR_DDR_BACKUP */ /* ----------------------------------------------------------------------------- * Root device handling @@ -752,9 +745,7 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain) return ret; domain->context_id = ret; -#ifdef CONFIG_RCAR_DDR_BACKUP domain->mmu->root->reg_backup[ret] = root_pgtable[ret]; -#endif domain->iop = alloc_io_pgtable_ops(ARM_32_LPAE_S1, &domain->cfg, domain); @@ -828,9 +819,7 @@ static void ipmmu_domain_destroy_context(struct ipmmu_vmsa_domain *domain) ipmmu_ctx_write_all(domain, IMCTR, IMCTR_FLUSH); ipmmu_tlb_sync(domain); -#ifdef CONFIG_RCAR_DDR_BACKUP domain->mmu->root->reg_backup[domain->context_id] = NULL; -#endif ipmmu_domain_free_context(domain->mmu->root, domain->context_id); } @@ -1092,6 +1081,13 @@ static bool ipmmu_slave_whitelist(struct device *dev, u32 *ids) goto exit; } + /* + * For R-Car Gen3 SoCs, use a white list to check the uTLB set up + * For other SoCs, return true + */ + if (!mmu->features->whitelist) + return true; + if (!mmu->whitelist[0]) { pr_debug("%s Whitelist not found on %s!!!\n", __func__, dev_name(mmu->dev)); @@ -1245,15 +1241,25 @@ error: return ret; } +static struct device *ipmmu_get_pci_host_device(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pci_bus *bus = pdev->bus; + + /* Walk up to the root bus to look for PCI Host controller */ + while (!pci_is_root_bus(bus)) + bus = bus->parent; + + return bus->bridge->parent; +} + static int ipmmu_add_device(struct device *dev) { + struct device *root_dev; struct iommu_group *group; -#ifdef CONFIG_RCAR_DDR_BACKUP - struct ipmmu_vmsa_backup *dev_backup; - struct iommu_fwspec *fwspec; - struct ipmmu_vmsa_device *mmu; + struct ipmmu_vmsa_backup *backup_data; + struct iommu_fwspec *fwspec = dev->iommu_fwspec; unsigned int *utlbs_val, *asids_val; -#endif /* * Only let through devices that have been verified in xlate() @@ -1264,56 +1270,69 @@ static int ipmmu_add_device(struct device *dev) if (IS_ENABLED(CONFIG_ARM) && !IS_ENABLED(CONFIG_IOMMU_DMA)) return ipmmu_init_arm_mapping(dev); + /* + * The IOMMU can't distinguish between different PCI Functions. + * Use PCI Host controller as a proxy for all connected PCI devices + */ + if (dev_is_pci(dev)) { + root_dev = ipmmu_get_pci_host_device(dev); + + if (root_dev->iommu_group) + dev->iommu_group = root_dev->iommu_group; + } + group = iommu_group_get_for_dev(dev); if (IS_ERR(group)) return PTR_ERR(group); iommu_group_put(group); -#ifdef CONFIG_RCAR_DDR_BACKUP - dev_backup = kzalloc(sizeof(*dev_backup), GFP_KERNEL); - dev_backup->dev = dev; - fwspec = dev->iommu_fwspec; - mmu = to_ipmmu(dev); - - spin_lock(&ipmmu_devices_backup_lock); - list_add(&dev_backup->list, &ipmmu_devices_backup); - spin_unlock(&ipmmu_devices_backup_lock); - + /* + * Backup data + */ utlbs_val = kcalloc(fwspec->num_ids, sizeof(*utlbs_val), GFP_KERNEL); if (!utlbs_val) return -ENOMEM; asids_val = kcalloc(fwspec->num_ids, sizeof(*asids_val), GFP_KERNEL); - if (!asids_val) + if (!asids_val) { + kfree(utlbs_val); return -ENOMEM; + } - mmu->utlbs_val = utlbs_val; - mmu->asids_val = asids_val; -#endif + backup_data = kzalloc(sizeof(*backup_data), GFP_KERNEL); + if (!backup_data) { + kfree(utlbs_val); + kfree(asids_val); + return -ENOMEM; + } + + backup_data->dev = dev; + backup_data->utlbs_val = utlbs_val; + backup_data->asids_val = asids_val; + + spin_lock(&ipmmu_devices_backup_lock); + list_add(&backup_data->list, &ipmmu_devices_backup); + spin_unlock(&ipmmu_devices_backup_lock); return 0; } static void ipmmu_remove_device(struct device *dev) { -#ifdef CONFIG_RCAR_DDR_BACKUP - struct ipmmu_vmsa_device *mmu = to_ipmmu(dev); - struct ipmmu_vmsa_backup *slave_dev; + struct ipmmu_vmsa_backup *backup_data; spin_lock(&ipmmu_devices_backup_lock); - list_for_each_entry(slave_dev, &ipmmu_devices_backup, list) { - if (slave_dev->dev == dev) { - list_del(&slave_dev->list); - kfree(slave_dev); + list_for_each_entry(backup_data, &ipmmu_devices_backup, list) { + if (backup_data->dev == dev) { + list_del(&backup_data->list); + kfree(backup_data->utlbs_val); + kfree(backup_data->asids_val); + kfree(backup_data); } } spin_unlock(&ipmmu_devices_backup_lock); - kfree(mmu->utlbs_val); - kfree(mmu->asids_val); -#endif - arm_iommu_detach_device(dev); iommu_group_remove_device(dev); } @@ -1609,37 +1628,36 @@ static int ipmmu_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP -#ifdef CONFIG_RCAR_DDR_BACKUP static int ipmmu_utlbs_backup(struct ipmmu_vmsa_device *mmu) { unsigned int i; struct ipmmu_vmsa_device *slave_mmu = NULL; - struct ipmmu_vmsa_backup *slave_dev = NULL; + struct ipmmu_vmsa_backup *backup_data = NULL; struct iommu_fwspec *slave_fwspec = NULL; pr_debug("%s: Handle UTLB backup\n", dev_name(mmu->dev)); spin_lock(&ipmmu_devices_backup_lock); - list_for_each_entry(slave_dev, &ipmmu_devices_backup, list) { - slave_mmu = to_ipmmu(slave_dev->dev); - slave_fwspec = slave_dev->dev->iommu_fwspec; + list_for_each_entry(backup_data, &ipmmu_devices_backup, list) { + slave_mmu = to_ipmmu(backup_data->dev); + slave_fwspec = backup_data->dev->iommu_fwspec; if (slave_mmu != mmu) continue; for (i = 0; i < slave_fwspec->num_ids; ++i) { - slave_mmu->utlbs_val[i] = + backup_data->utlbs_val[i] = ipmmu_read(slave_mmu, IMUCTR(slave_fwspec->ids[i])); - slave_mmu->asids_val[i] = + backup_data->asids_val[i] = ipmmu_read(slave_mmu, IMUASID(slave_fwspec->ids[i])); pr_debug("%d: Backup UTLB[%d]: 0x%x, ASID[%d]: %d\n", i, slave_fwspec->ids[i], - slave_mmu->utlbs_val[i], + backup_data->utlbs_val[i], slave_fwspec->ids[i], - slave_mmu->asids_val[i]); + backup_data->asids_val[i]); } } @@ -1652,16 +1670,16 @@ static int ipmmu_utlbs_restore(struct ipmmu_vmsa_device *mmu) { unsigned int i; struct ipmmu_vmsa_device *slave_mmu = NULL; - struct ipmmu_vmsa_backup *slave_dev = NULL; + struct ipmmu_vmsa_backup *backup_data = NULL; struct iommu_fwspec *slave_fwspec = NULL; pr_debug("%s: Handle UTLB restore\n", dev_name(mmu->dev)); spin_lock(&ipmmu_devices_backup_lock); - list_for_each_entry(slave_dev, &ipmmu_devices_backup, list) { - slave_mmu = to_ipmmu(slave_dev->dev); - slave_fwspec = slave_dev->dev->iommu_fwspec; + list_for_each_entry(backup_data, &ipmmu_devices_backup, list) { + slave_mmu = to_ipmmu(backup_data->dev); + slave_fwspec = backup_data->dev->iommu_fwspec; if (slave_mmu != mmu) continue; @@ -1669,10 +1687,10 @@ static int ipmmu_utlbs_restore(struct ipmmu_vmsa_device *mmu) for (i = 0; i < slave_fwspec->num_ids; ++i) { ipmmu_write(slave_mmu, IMUASID(slave_fwspec->ids[i]), - slave_mmu->asids_val[i]); + backup_data->asids_val[i]); ipmmu_write(slave_mmu, IMUCTR(slave_fwspec->ids[i]), - slave_mmu->utlbs_val[i]); + backup_data->utlbs_val[i]); pr_debug("%d: Restore UTLB[%d]: 0x%x, ASID[%d]: %d\n", i, slave_fwspec->ids[i], ipmmu_read(slave_mmu, @@ -1741,11 +1759,9 @@ static int ipmmu_domain_restore_context(struct ipmmu_vmsa_domain *domain) return 0; } -#endif /* CONFIG_RCAR_DDR_BACKUP */ static int ipmmu_suspend(struct device *dev) { -#ifdef CONFIG_RCAR_DDR_BACKUP unsigned int i; struct ipmmu_vmsa_device *mmu = dev_get_drvdata(dev); @@ -1761,14 +1777,12 @@ static int ipmmu_suspend(struct device *dev) } else ipmmu_utlbs_backup(mmu); -#endif return 0; } static int ipmmu_resume(struct device *dev) { -#ifdef CONFIG_RCAR_DDR_BACKUP unsigned int i; struct ipmmu_vmsa_device *mmu = dev_get_drvdata(dev); @@ -1782,7 +1796,6 @@ static int ipmmu_resume(struct device *dev) } else ipmmu_utlbs_restore(mmu); -#endif return 0; } |