summaryrefslogtreecommitdiff
path: root/drivers/iommu
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iommu')
-rw-r--r--drivers/iommu/amd_iommu.c3
-rw-r--r--drivers/iommu/amd_iommu_init.c9
-rw-r--r--drivers/iommu/amd_iommu_v2.c4
-rw-r--r--drivers/iommu/intel-iommu.c2
-rw-r--r--drivers/iommu/intel-svm.c1
-rw-r--r--drivers/iommu/tegra-smmu.c124
6 files changed, 132 insertions, 11 deletions
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index dc4b73833419..74788fdeb773 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -527,7 +527,8 @@ static void amd_iommu_report_page_fault(u16 devid, u16 domain_id,
struct iommu_dev_data *dev_data = NULL;
struct pci_dev *pdev;
- pdev = pci_get_bus_and_slot(PCI_BUS_NUM(devid), devid & 0xff);
+ pdev = pci_get_domain_bus_and_slot(0, PCI_BUS_NUM(devid),
+ devid & 0xff);
if (pdev)
dev_data = get_dev_data(&pdev->dev);
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 6fe2d0346073..4e4a615bf13f 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -1697,8 +1697,8 @@ static int iommu_init_pci(struct amd_iommu *iommu)
u32 range, misc, low, high;
int ret;
- iommu->dev = pci_get_bus_and_slot(PCI_BUS_NUM(iommu->devid),
- iommu->devid & 0xff);
+ iommu->dev = pci_get_domain_bus_and_slot(0, PCI_BUS_NUM(iommu->devid),
+ iommu->devid & 0xff);
if (!iommu->dev)
return -ENODEV;
@@ -1764,8 +1764,9 @@ static int iommu_init_pci(struct amd_iommu *iommu)
if (is_rd890_iommu(iommu->dev)) {
int i, j;
- iommu->root_pdev = pci_get_bus_and_slot(iommu->dev->bus->number,
- PCI_DEVFN(0, 0));
+ iommu->root_pdev =
+ pci_get_domain_bus_and_slot(0, iommu->dev->bus->number,
+ PCI_DEVFN(0, 0));
/*
* Some rd890 systems may not be fully reconfigured by the
diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c
index 7d94e1d39e5e..1d0b53a04a08 100644
--- a/drivers/iommu/amd_iommu_v2.c
+++ b/drivers/iommu/amd_iommu_v2.c
@@ -427,6 +427,7 @@ static void mn_release(struct mmu_notifier *mn, struct mm_struct *mm)
}
static const struct mmu_notifier_ops iommu_mn = {
+ .flags = MMU_INVALIDATE_DOES_NOT_BLOCK,
.release = mn_release,
.clear_flush_young = mn_clear_flush_young,
.invalidate_range = mn_invalidate_range,
@@ -564,7 +565,8 @@ static int ppr_notifier(struct notifier_block *nb, unsigned long e, void *data)
finish = (iommu_fault->tag >> 9) & 1;
devid = iommu_fault->device_id;
- pdev = pci_get_bus_and_slot(PCI_BUS_NUM(devid), devid & 0xff);
+ pdev = pci_get_domain_bus_and_slot(0, PCI_BUS_NUM(devid),
+ devid & 0xff);
if (!pdev)
return -ENODEV;
dev_data = get_dev_data(&pdev->dev);
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 53227d6e911e..582fd01cb7d1 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -4807,7 +4807,7 @@ int __init intel_iommu_init(void)
up_write(&dmar_global_lock);
pr_info("Intel(R) Virtualization Technology for Directed I/O\n");
-#ifdef CONFIG_SWIOTLB
+#if defined(CONFIG_X86) && defined(CONFIG_SWIOTLB)
swiotlb = 0;
#endif
dma_ops = &intel_dma_ops;
diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c
index 859b61ddd508..35a408d0ae4f 100644
--- a/drivers/iommu/intel-svm.c
+++ b/drivers/iommu/intel-svm.c
@@ -291,6 +291,7 @@ static void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm)
}
static const struct mmu_notifier_ops intel_mmuops = {
+ .flags = MMU_INVALIDATE_DOES_NOT_BLOCK,
.release = intel_mm_release,
.change_pte = intel_change_pte,
.invalidate_range = intel_invalidate_range,
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 3b6449e2cbf1..44d40bc771b5 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -20,6 +20,12 @@
#include <soc/tegra/ahb.h>
#include <soc/tegra/mc.h>
+struct tegra_smmu_group {
+ struct list_head list;
+ const struct tegra_smmu_group_soc *soc;
+ struct iommu_group *group;
+};
+
struct tegra_smmu {
void __iomem *regs;
struct device *dev;
@@ -27,6 +33,8 @@ struct tegra_smmu {
struct tegra_mc *mc;
const struct tegra_smmu_soc *soc;
+ struct list_head groups;
+
unsigned long pfn_mask;
unsigned long tlb_mask;
@@ -703,19 +711,47 @@ static struct tegra_smmu *tegra_smmu_find(struct device_node *np)
return mc->smmu;
}
+static int tegra_smmu_configure(struct tegra_smmu *smmu, struct device *dev,
+ struct of_phandle_args *args)
+{
+ const struct iommu_ops *ops = smmu->iommu.ops;
+ int err;
+
+ err = iommu_fwspec_init(dev, &dev->of_node->fwnode, ops);
+ if (err < 0) {
+ dev_err(dev, "failed to initialize fwspec: %d\n", err);
+ return err;
+ }
+
+ err = ops->of_xlate(dev, args);
+ if (err < 0) {
+ dev_err(dev, "failed to parse SW group ID: %d\n", err);
+ iommu_fwspec_free(dev);
+ return err;
+ }
+
+ return 0;
+}
+
static int tegra_smmu_add_device(struct device *dev)
{
struct device_node *np = dev->of_node;
+ struct tegra_smmu *smmu = NULL;
struct iommu_group *group;
struct of_phandle_args args;
unsigned int index = 0;
+ int 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) {
+ err = tegra_smmu_configure(smmu, dev, &args);
+ of_node_put(args.np);
+
+ if (err < 0)
+ return err;
+
/*
* Only a single IOMMU master interface is currently
* supported by the Linux kernel, so abort after the
@@ -728,9 +764,13 @@ static int tegra_smmu_add_device(struct device *dev)
break;
}
+ of_node_put(args.np);
index++;
}
+ if (!smmu)
+ return -ENODEV;
+
group = iommu_group_get_for_dev(dev);
if (IS_ERR(group))
return PTR_ERR(group);
@@ -751,6 +791,80 @@ static void tegra_smmu_remove_device(struct device *dev)
iommu_group_remove_device(dev);
}
+static const struct tegra_smmu_group_soc *
+tegra_smmu_find_group(struct tegra_smmu *smmu, unsigned int swgroup)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < smmu->soc->num_groups; i++)
+ for (j = 0; j < smmu->soc->groups[i].num_swgroups; j++)
+ if (smmu->soc->groups[i].swgroups[j] == swgroup)
+ return &smmu->soc->groups[i];
+
+ return NULL;
+}
+
+static struct iommu_group *tegra_smmu_group_get(struct tegra_smmu *smmu,
+ unsigned int swgroup)
+{
+ const struct tegra_smmu_group_soc *soc;
+ struct tegra_smmu_group *group;
+
+ soc = tegra_smmu_find_group(smmu, swgroup);
+ if (!soc)
+ return NULL;
+
+ mutex_lock(&smmu->lock);
+
+ list_for_each_entry(group, &smmu->groups, list)
+ if (group->soc == soc) {
+ mutex_unlock(&smmu->lock);
+ return group->group;
+ }
+
+ group = devm_kzalloc(smmu->dev, sizeof(*group), GFP_KERNEL);
+ if (!group) {
+ mutex_unlock(&smmu->lock);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&group->list);
+ group->soc = soc;
+
+ group->group = iommu_group_alloc();
+ if (IS_ERR(group->group)) {
+ devm_kfree(smmu->dev, group);
+ mutex_unlock(&smmu->lock);
+ return NULL;
+ }
+
+ list_add_tail(&group->list, &smmu->groups);
+ mutex_unlock(&smmu->lock);
+
+ return group->group;
+}
+
+static struct iommu_group *tegra_smmu_device_group(struct device *dev)
+{
+ struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+ struct tegra_smmu *smmu = dev->archdata.iommu;
+ struct iommu_group *group;
+
+ group = tegra_smmu_group_get(smmu, fwspec->ids[0]);
+ if (!group)
+ group = generic_device_group(dev);
+
+ return group;
+}
+
+static int tegra_smmu_of_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ u32 id = args->args[0];
+
+ return iommu_fwspec_add_ids(dev, &id, 1);
+}
+
static const struct iommu_ops tegra_smmu_ops = {
.capable = tegra_smmu_capable,
.domain_alloc = tegra_smmu_domain_alloc,
@@ -759,12 +873,12 @@ static const struct iommu_ops tegra_smmu_ops = {
.detach_dev = tegra_smmu_detach_dev,
.add_device = tegra_smmu_add_device,
.remove_device = tegra_smmu_remove_device,
- .device_group = generic_device_group,
+ .device_group = tegra_smmu_device_group,
.map = tegra_smmu_map,
.unmap = tegra_smmu_unmap,
.map_sg = default_iommu_map_sg,
.iova_to_phys = tegra_smmu_iova_to_phys,
-
+ .of_xlate = tegra_smmu_of_xlate,
.pgsize_bitmap = SZ_4K,
};
@@ -913,6 +1027,7 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
if (!smmu->asids)
return ERR_PTR(-ENOMEM);
+ INIT_LIST_HEAD(&smmu->groups);
mutex_init(&smmu->lock);
smmu->regs = mc->regs;
@@ -954,6 +1069,7 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
return ERR_PTR(err);
iommu_device_set_ops(&smmu->iommu, &tegra_smmu_ops);
+ iommu_device_set_fwnode(&smmu->iommu, dev->fwnode);
err = iommu_device_register(&smmu->iommu);
if (err) {