summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@quicinc.com>2017-05-30 16:04:25 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2017-05-30 16:04:24 -0700
commit4cc716041e2eedc07824e725966dbff29abd7fb6 (patch)
tree81d1d1eb2103c6fcce983725bf4cd687f406616c
parent22d89182eaf9ce50c14199e796abc8c4a89d13ff (diff)
parent3b3b2f468caca6415b72de0df3c37552db33bf55 (diff)
Merge "iommu/arm-smmu: Optimized IOMMUS property parsing"
-rw-r--r--drivers/iommu/arm-smmu.c153
1 files changed, 114 insertions, 39 deletions
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index dc44b40a85f3..065379c1397f 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -497,6 +497,19 @@ struct static_cbndx_entry {
u8 type;
};
+struct arm_iommus_node {
+ struct device_node *master;
+ struct list_head list;
+ struct list_head iommuspec_list;
+};
+
+struct arm_iommus_spec {
+ struct of_phandle_args iommu_spec;
+ struct list_head list;
+};
+
+static LIST_HEAD(iommus_nodes);
+
static int arm_smmu_enable_clocks_atomic(struct arm_smmu_device *smmu);
static void arm_smmu_disable_clocks_atomic(struct arm_smmu_device *smmu);
static void arm_smmu_prepare_pgtable(void *addr, void *cookie);
@@ -683,39 +696,40 @@ static int register_smmu_master(struct arm_smmu_device *smmu,
return insert_smmu_master(smmu, master);
}
-static int arm_smmu_parse_iommus_properties(struct arm_smmu_device *smmu,
- int *num_masters)
+static int arm_smmu_parse_iommus_properties(struct arm_smmu_device *smmu)
{
- struct of_phandle_args iommuspec;
- struct device_node *master;
-
- *num_masters = 0;
+ struct arm_iommus_node *node, *nex;
- for_each_node_with_property(master, "iommus") {
- int arg_ind = 0;
- struct iommus_entry *entry, *n;
+ list_for_each_entry_safe(node, nex, &iommus_nodes, list) {
+ struct iommus_entry *entry, *next;
+ struct arm_iommus_spec *iommuspec_node, *n;
LIST_HEAD(iommus);
+ int node_found = 0;
- while (!of_parse_phandle_with_args(
- master, "iommus", "#iommu-cells",
- arg_ind, &iommuspec)) {
- if (iommuspec.np != smmu->dev->of_node) {
- arg_ind++;
+ list_for_each_entry_safe(iommuspec_node, n,
+ &node->iommuspec_list, list) {
+ if (iommuspec_node->iommu_spec.np != smmu->dev->of_node)
continue;
- }
+
+ /*
+ * Since each master node will have iommu spec(s) of the
+ * same device, we can delete this master node after
+ * the devices are registered.
+ */
+ node_found = 1;
list_for_each_entry(entry, &iommus, list)
- if (entry->node == master)
+ if (entry->node == node->master)
break;
if (&entry->list == &iommus) {
entry = devm_kzalloc(smmu->dev, sizeof(*entry),
GFP_KERNEL);
if (!entry)
return -ENOMEM;
- entry->node = master;
+ entry->node = node->master;
list_add(&entry->list, &iommus);
}
- switch (iommuspec.args_count) {
+ switch (iommuspec_node->iommu_spec.args_count) {
case 0:
/*
* For pci-e devices the SIDs are provided
@@ -725,25 +739,29 @@ static int arm_smmu_parse_iommus_properties(struct arm_smmu_device *smmu,
case 1:
entry->num_sids++;
entry->streamids[entry->num_sids - 1]
- = iommuspec.args[0];
+ = iommuspec_node->iommu_spec.args[0];
break;
default:
BUG();
}
- arg_ind++;
+ list_del(&iommuspec_node->list);
+ kfree(iommuspec_node);
}
- list_for_each_entry_safe(entry, n, &iommus, list) {
+ list_for_each_entry_safe(entry, next, &iommus, list) {
int rc = register_smmu_master(smmu, entry);
- if (rc) {
+
+ if (rc)
dev_err(smmu->dev, "Couldn't register %s\n",
- entry->node->name);
- } else {
- (*num_masters)++;
- }
+ entry->node->name);
list_del(&entry->list);
devm_kfree(smmu->dev, entry);
}
+
+ if (node_found) {
+ list_del(&node->list);
+ kfree(node);
+ }
}
return 0;
@@ -3973,7 +3991,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
struct arm_smmu_device *smmu;
struct device *dev = &pdev->dev;
struct rb_node *node;
- int num_irqs, i, err, num_masters;
+ int num_irqs, i, err;
smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
if (!smmu) {
@@ -4036,33 +4054,33 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
}
i = 0;
- smmu->masters = RB_ROOT;
- err = arm_smmu_parse_iommus_properties(smmu, &num_masters);
- if (err)
- goto out_put_masters;
-
- dev_dbg(dev, "registered %d master devices\n", num_masters);
err = arm_smmu_parse_impl_def_registers(smmu);
if (err)
- goto out_put_masters;
+ goto out;
err = arm_smmu_init_regulators(smmu);
if (err)
- goto out_put_masters;
+ goto out;
err = arm_smmu_init_clocks(smmu);
if (err)
- goto out_put_masters;
+ goto out;
err = arm_smmu_init_bus_scaling(pdev, smmu);
if (err)
- goto out_put_masters;
+ goto out;
parse_driver_options(smmu);
err = arm_smmu_enable_clocks(smmu);
if (err)
+ goto out;
+
+ /* No probe deferral occurred! Proceed with iommu property parsing. */
+ smmu->masters = RB_ROOT;
+ err = arm_smmu_parse_iommus_properties(smmu);
+ if (err)
goto out_put_masters;
smmu->sec_id = msm_dev_to_device_id(dev);
@@ -4122,7 +4140,7 @@ out_put_masters:
= container_of(node, struct arm_smmu_master, node);
of_node_put(master->of_node);
}
-
+out:
return err;
}
@@ -4173,6 +4191,58 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
return 0;
}
+static void arm_smmu_free_master_nodes(void)
+{
+ struct arm_iommus_node *node, *nex;
+ struct arm_iommus_spec *entry, *n;
+
+ list_for_each_entry_safe(node, nex, &iommus_nodes, list) {
+ list_for_each_entry_safe(entry, n,
+ &node->iommuspec_list, list) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ list_del(&node->list);
+ kfree(node);
+ }
+}
+
+static int arm_smmu_get_master_nodes(void)
+{
+ struct arm_iommus_node *node;
+ struct device_node *master;
+ struct of_phandle_args iommuspec;
+ struct arm_iommus_spec *entry;
+
+ for_each_node_with_property(master, "iommus") {
+ int arg_ind = 0;
+
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node)
+ goto release_memory;
+ node->master = master;
+ list_add(&node->list, &iommus_nodes);
+
+ INIT_LIST_HEAD(&node->iommuspec_list);
+
+ while (!of_parse_phandle_with_args(master, "iommus",
+ "#iommu-cells", arg_ind, &iommuspec)) {
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ goto release_memory;
+ entry->iommu_spec = iommuspec;
+ list_add(&entry->list, &node->iommuspec_list);
+ arg_ind++;
+ }
+ }
+
+ return 0;
+
+release_memory:
+ arm_smmu_free_master_nodes();
+ return -ENOMEM;
+}
+
static struct platform_driver arm_smmu_driver = {
.driver = {
.name = "arm-smmu",
@@ -4198,10 +4268,15 @@ static int __init arm_smmu_init(void)
of_node_put(np);
- ret = platform_driver_register(&arm_smmu_driver);
+ ret = arm_smmu_get_master_nodes();
if (ret)
return ret;
+ ret = platform_driver_register(&arm_smmu_driver);
+ if (ret) {
+ arm_smmu_free_master_nodes();
+ return ret;
+ }
/* Oh, for a proper bus abstraction */
if (!iommu_present(&platform_bus_type))
bus_set_iommu(&platform_bus_type, &arm_smmu_ops);