summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/iommu/arm-smmu.c370
1 files changed, 101 insertions, 269 deletions
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 69b6cab65421..2023a77015a0 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -317,18 +317,13 @@ struct arm_smmu_smr {
};
struct arm_smmu_master_cfg {
+ struct arm_smmu_device *smmu;
int num_streamids;
u16 streamids[MAX_MASTER_STREAMIDS];
s16 smendx[MAX_MASTER_STREAMIDS];
};
#define INVALID_SMENDX -1
-struct arm_smmu_master {
- struct device_node *of_node;
- struct rb_node node;
- struct arm_smmu_master_cfg cfg;
-};
-
struct arm_smmu_device {
struct device *dev;
@@ -376,7 +371,6 @@ struct arm_smmu_device {
unsigned int *irqs;
struct list_head list;
- struct rb_root masters;
u32 cavium_id_base; /* Specific to Cavium */
};
@@ -415,12 +409,6 @@ struct arm_smmu_domain {
struct iommu_domain domain;
};
-struct arm_smmu_phandle_args {
- struct device_node *np;
- int args_count;
- uint32_t args[MAX_MASTER_STREAMIDS];
-};
-
static DEFINE_SPINLOCK(arm_smmu_devices_lock);
static LIST_HEAD(arm_smmu_devices);
@@ -462,132 +450,89 @@ static struct device_node *dev_get_dev_node(struct device *dev)
while (!pci_is_root_bus(bus))
bus = bus->parent;
- return bus->bridge->parent->of_node;
+ return of_node_get(bus->bridge->parent->of_node);
}
- return dev->of_node;
+ return of_node_get(dev->of_node);
}
-static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
- struct device_node *dev_node)
+static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
{
- struct rb_node *node = smmu->masters.rb_node;
-
- while (node) {
- struct arm_smmu_master *master;
-
- master = container_of(node, struct arm_smmu_master, node);
-
- if (dev_node < master->of_node)
- node = node->rb_left;
- else if (dev_node > master->of_node)
- node = node->rb_right;
- else
- return master;
- }
-
- return NULL;
+ *((__be32 *)data) = cpu_to_be32(alias);
+ return 0; /* Continue walking */
}
-static struct arm_smmu_master_cfg *
-find_smmu_master_cfg(struct device *dev)
+static int __find_legacy_master_phandle(struct device *dev, void *data)
{
- struct arm_smmu_master_cfg *cfg = NULL;
- struct iommu_group *group = iommu_group_get(dev);
-
- if (group) {
- cfg = iommu_group_get_iommudata(group);
- iommu_group_put(group);
- }
-
- return cfg;
+ struct of_phandle_iterator *it = *(void **)data;
+ struct device_node *np = it->node;
+ int err;
+
+ of_for_each_phandle(it, err, dev->of_node, "mmu-masters",
+ "#stream-id-cells", 0)
+ if (it->node == np) {
+ *(void **)data = dev;
+ return 1;
+ }
+ it->node = np;
+ return err == -ENOENT ? 0 : err;
}
-static int insert_smmu_master(struct arm_smmu_device *smmu,
- struct arm_smmu_master *master)
+static int arm_smmu_register_legacy_master(struct device *dev)
{
- struct rb_node **new, *parent;
-
- new = &smmu->masters.rb_node;
- parent = NULL;
- while (*new) {
- struct arm_smmu_master *this
- = container_of(*new, struct arm_smmu_master, node);
-
- parent = *new;
- if (master->of_node < this->of_node)
- new = &((*new)->rb_left);
- else if (master->of_node > this->of_node)
- new = &((*new)->rb_right);
- else
- return -EEXIST;
- }
-
- rb_link_node(&master->node, parent, new);
- rb_insert_color(&master->node, &smmu->masters);
- return 0;
-}
+ struct arm_smmu_device *smmu;
+ struct arm_smmu_master_cfg *cfg;
+ struct device_node *np;
+ struct of_phandle_iterator it;
+ void *data = &it;
+ __be32 pci_sid;
+ int err;
-static int register_smmu_master(struct arm_smmu_device *smmu,
- struct device *dev,
- struct arm_smmu_phandle_args *masterspec)
-{
- int i;
- struct arm_smmu_master *master;
+ np = dev_get_dev_node(dev);
+ if (!np || !of_find_property(np, "#stream-id-cells", NULL)) {
+ of_node_put(np);
+ return -ENODEV;
+ }
- master = find_smmu_master(smmu, masterspec->np);
- if (master) {
- dev_err(dev,
- "rejecting multiple registrations for master device %s\n",
- masterspec->np->name);
- return -EBUSY;
+ it.node = np;
+ spin_lock(&arm_smmu_devices_lock);
+ list_for_each_entry(smmu, &arm_smmu_devices, list) {
+ err = __find_legacy_master_phandle(smmu->dev, &data);
+ if (err)
+ break;
}
+ spin_unlock(&arm_smmu_devices_lock);
+ of_node_put(np);
+ if (err == 0)
+ return -ENODEV;
+ if (err < 0)
+ return err;
- if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
- dev_err(dev,
+ if (it.cur_count > MAX_MASTER_STREAMIDS) {
+ dev_err(smmu->dev,
"reached maximum number (%d) of stream IDs for master device %s\n",
- MAX_MASTER_STREAMIDS, masterspec->np->name);
+ MAX_MASTER_STREAMIDS, dev_name(dev));
return -ENOSPC;
}
+ if (dev_is_pci(dev)) {
+ /* "mmu-masters" assumes Stream ID == Requester ID */
+ pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid,
+ &pci_sid);
+ it.cur = &pci_sid;
+ it.cur_count = 1;
+ }
- master = devm_kzalloc(dev, sizeof(*master), GFP_KERNEL);
- if (!master)
+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+ if (!cfg)
return -ENOMEM;
- master->of_node = masterspec->np;
- master->cfg.num_streamids = masterspec->args_count;
-
- for (i = 0; i < master->cfg.num_streamids; ++i) {
- u16 streamid = masterspec->args[i];
-
- if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) &&
- (streamid >= smmu->num_mapping_groups)) {
- dev_err(dev,
- "stream ID for master device %s greater than maximum allowed (%d)\n",
- masterspec->np->name, smmu->num_mapping_groups);
- return -ERANGE;
- }
- master->cfg.streamids[i] = streamid;
- master->cfg.smendx[i] = INVALID_SMENDX;
- }
- return insert_smmu_master(smmu, master);
-}
-
-static struct arm_smmu_device *find_smmu_for_device(struct device *dev)
-{
- struct arm_smmu_device *smmu;
- struct arm_smmu_master *master = NULL;
- struct device_node *dev_node = dev_get_dev_node(dev);
+ cfg->smmu = smmu;
+ dev->archdata.iommu = cfg;
- spin_lock(&arm_smmu_devices_lock);
- list_for_each_entry(smmu, &arm_smmu_devices, list) {
- master = find_smmu_master(smmu, dev_node);
- if (master)
- break;
- }
- spin_unlock(&arm_smmu_devices_lock);
+ while (it.cur_count--)
+ cfg->streamids[cfg->num_streamids++] = be32_to_cpup(it.cur++);
- return master ? smmu : NULL;
+ return 0;
}
static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
@@ -1119,8 +1064,7 @@ static void arm_smmu_free_smr(struct arm_smmu_device *smmu, int idx)
static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
{
struct arm_smmu_smr *smr = smmu->smrs + idx;
- u32 reg = (smr->id & smmu->streamid_mask) << SMR_ID_SHIFT |
- (smr->mask & smmu->smr_mask_mask) << SMR_MASK_SHIFT;
+ u32 reg = smr->id << SMR_ID_SHIFT | smr->mask << SMR_MASK_SHIFT;
if (smr->valid)
reg |= SMR_VALID;
@@ -1189,9 +1133,9 @@ err_free_smrs:
return -ENOSPC;
}
-static void arm_smmu_master_free_smes(struct arm_smmu_device *smmu,
- struct arm_smmu_master_cfg *cfg)
+static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg)
{
+ struct arm_smmu_device *smmu = cfg->smmu;
int i;
/*
@@ -1262,17 +1206,15 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
{
int ret;
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
- struct arm_smmu_device *smmu;
- struct arm_smmu_master_cfg *cfg;
+ struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
- smmu = find_smmu_for_device(dev);
- if (!smmu) {
+ if (!cfg) {
dev_err(dev, "cannot attach to SMMU, is it on the same bus?\n");
return -ENXIO;
}
/* Ensure that the domain is finalised */
- ret = arm_smmu_init_domain_context(domain, smmu);
+ ret = arm_smmu_init_domain_context(domain, cfg->smmu);
if (ret < 0)
return ret;
@@ -1280,18 +1222,14 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
* Sanity check the domain. We don't support domains across
* different SMMUs.
*/
- if (smmu_domain->smmu != smmu) {
+ if (smmu_domain->smmu != cfg->smmu) {
dev_err(dev,
"cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n",
- dev_name(smmu_domain->smmu->dev), dev_name(smmu->dev));
+ dev_name(smmu_domain->smmu->dev), dev_name(cfg->smmu->dev));
return -EINVAL;
}
/* Looks ok, so add the device to the domain */
- cfg = find_smmu_master_cfg(dev);
- if (!cfg)
- return -ENODEV;
-
return arm_smmu_domain_add_master(smmu_domain, cfg);
}
@@ -1411,120 +1349,65 @@ static bool arm_smmu_capable(enum iommu_cap cap)
}
}
-static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
-{
- *((u16 *)data) = alias;
- return 0; /* Continue walking */
-}
-
-static void __arm_smmu_release_pci_iommudata(void *data)
-{
- kfree(data);
-}
-
-static int arm_smmu_init_pci_device(struct pci_dev *pdev,
- struct iommu_group *group)
+static int arm_smmu_add_device(struct device *dev)
{
struct arm_smmu_master_cfg *cfg;
- u16 sid;
- int i;
-
- cfg = iommu_group_get_iommudata(group);
- if (!cfg) {
- cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
- if (!cfg)
- return -ENOMEM;
-
- iommu_group_set_iommudata(group, cfg,
- __arm_smmu_release_pci_iommudata);
- }
+ struct iommu_group *group;
+ int i, ret;
- if (cfg->num_streamids >= MAX_MASTER_STREAMIDS)
- return -ENOSPC;
+ ret = arm_smmu_register_legacy_master(dev);
+ cfg = dev->archdata.iommu;
+ if (ret)
+ goto out_free;
- /*
- * Assume Stream ID == Requester ID for now.
- * We need a way to describe the ID mappings in FDT.
- */
- pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
- for (i = 0; i < cfg->num_streamids; ++i)
- if (cfg->streamids[i] == sid)
- break;
+ ret = -EINVAL;
+ for (i = 0; i < cfg->num_streamids; i++) {
+ u16 sid = cfg->streamids[i];
- /* Avoid duplicate SIDs, as this can lead to SMR conflicts */
- if (i == cfg->num_streamids) {
- cfg->streamids[i] = sid;
+ if (sid & ~cfg->smmu->streamid_mask) {
+ dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
+ sid, cfg->smmu->streamid_mask);
+ goto out_free;
+ }
cfg->smendx[i] = INVALID_SMENDX;
- cfg->num_streamids++;
}
- return 0;
-}
-
-static int arm_smmu_init_platform_device(struct device *dev,
- struct iommu_group *group)
-{
- struct arm_smmu_device *smmu = find_smmu_for_device(dev);
- struct arm_smmu_master *master;
-
- if (!smmu)
- return -ENODEV;
-
- master = find_smmu_master(smmu, dev->of_node);
- if (!master)
- return -ENODEV;
-
- iommu_group_set_iommudata(group, &master->cfg, NULL);
-
- return 0;
-}
-
-static int arm_smmu_add_device(struct device *dev)
-{
- struct iommu_group *group;
-
group = iommu_group_get_for_dev(dev);
- if (IS_ERR(group))
- return PTR_ERR(group);
-
+ if (IS_ERR(group)) {
+ ret = PTR_ERR(group);
+ goto out_free;
+ }
iommu_group_put(group);
return 0;
+
+out_free:
+ kfree(cfg);
+ dev->archdata.iommu = NULL;
+ return ret;
}
static void arm_smmu_remove_device(struct device *dev)
{
- struct arm_smmu_device *smmu = find_smmu_for_device(dev);
- struct arm_smmu_master_cfg *cfg = find_smmu_master_cfg(dev);
+ struct arm_smmu_master_cfg *cfg = dev->archdata.iommu;
- if (smmu && cfg)
- arm_smmu_master_free_smes(smmu, cfg);
+ if (!cfg)
+ return;
+ arm_smmu_master_free_smes(cfg);
iommu_group_remove_device(dev);
+ kfree(cfg);
+ dev->archdata.iommu = NULL;
}
static struct iommu_group *arm_smmu_device_group(struct device *dev)
{
struct iommu_group *group;
- int ret;
if (dev_is_pci(dev))
group = pci_device_group(dev);
else
group = generic_device_group(dev);
- if (IS_ERR(group))
- return group;
-
- if (dev_is_pci(dev))
- ret = arm_smmu_init_pci_device(to_pci_dev(dev), group);
- else
- ret = arm_smmu_init_platform_device(dev, group);
-
- if (ret) {
- iommu_group_put(group);
- group = ERR_PTR(ret);
- }
-
return group;
}
@@ -1938,9 +1821,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
struct resource *res;
struct arm_smmu_device *smmu;
struct device *dev = &pdev->dev;
- struct rb_node *node;
- struct of_phandle_iterator it;
- struct arm_smmu_phandle_args *masterspec;
int num_irqs, i, err;
smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
@@ -2001,37 +1881,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
if (err)
return err;
- i = 0;
- smmu->masters = RB_ROOT;
-
- err = -ENOMEM;
- /* No need to zero the memory for masterspec */
- masterspec = kmalloc(sizeof(*masterspec), GFP_KERNEL);
- if (!masterspec)
- goto out_put_masters;
-
- of_for_each_phandle(&it, err, dev->of_node,
- "mmu-masters", "#stream-id-cells", 0) {
- int count = of_phandle_iterator_args(&it, masterspec->args,
- MAX_MASTER_STREAMIDS);
- masterspec->np = of_node_get(it.node);
- masterspec->args_count = count;
-
- err = register_smmu_master(smmu, dev, masterspec);
- if (err) {
- dev_err(dev, "failed to add master %s\n",
- masterspec->np->name);
- kfree(masterspec);
- goto out_put_masters;
- }
-
- i++;
- }
-
- dev_notice(dev, "registered %d master devices\n", i);
-
- kfree(masterspec);
-
parse_driver_options(smmu);
if (smmu->version == ARM_SMMU_V2 &&
@@ -2039,8 +1888,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
dev_err(dev,
"found only %d context interrupt(s) but %d required\n",
smmu->num_context_irqs, smmu->num_context_banks);
- err = -ENODEV;
- goto out_put_masters;
+ return -ENODEV;
}
for (i = 0; i < smmu->num_global_irqs; ++i) {
@@ -2052,7 +1900,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
if (err) {
dev_err(dev, "failed to request global IRQ %d (%u)\n",
i, smmu->irqs[i]);
- goto out_put_masters;
+ return err;
}
}
@@ -2063,22 +1911,12 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
arm_smmu_device_reset(smmu);
return 0;
-
-out_put_masters:
- for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
- struct arm_smmu_master *master
- = container_of(node, struct arm_smmu_master, node);
- of_node_put(master->of_node);
- }
-
- return err;
}
static int arm_smmu_device_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct arm_smmu_device *curr, *smmu = NULL;
- struct rb_node *node;
spin_lock(&arm_smmu_devices_lock);
list_for_each_entry(curr, &arm_smmu_devices, list) {
@@ -2093,12 +1931,6 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
if (!smmu)
return -ENODEV;
- for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
- struct arm_smmu_master *master
- = container_of(node, struct arm_smmu_master, node);
- of_node_put(master->of_node);
- }
-
if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS))
dev_err(dev, "removing device with active domains!\n");