aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyo Kataoka <ryo.kataoka.wt@renesas.com>2019-03-22 20:50:35 +0900
committerRyo Kataoka <ryo.kataoka.wt@renesas.com>2019-03-22 20:50:35 +0900
commit538d41278caaf9b4066db035a11c4ef38f5912f3 (patch)
tree73ef298816a80a950bb49fda51145d7e0ecd0a28
parent442e4daf2ba7cee3749f0cc6c6f0b3d15f0804ed (diff)
parente2acafb18264fe3dd3c69e209ac96ab889277555 (diff)
downloadrenesas-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.c2
-rw-r--r--drivers/iommu/ipmmu-vmsa.c143
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;
}