[COMMON] iommu/exynos: of_xlate for master and sysmmu
authorJanghyuck Kim <janghyuck.kim@samsung.com>
Fri, 22 Apr 2016 11:13:54 +0000 (20:13 +0900)
committerSangwook Ju <sw.ju@samsung.com>
Mon, 14 May 2018 10:45:18 +0000 (19:45 +0900)
of_xlate is called when master device is added.
Purpose of of_xlate is making relationship between master and sysmmu
devices. Domain and client relationship is also created at this time.

Change-Id: I4b7f5a2d9d1237b61beee1a6f216ef39c60df722
Signed-off-by: Janghyuck Kim <janghyuck.kim@samsung.com>
drivers/iommu/exynos-iommu.c

index f8dca04ce26f2ccff52a1542ef1080c70db0ca87..aa368c1a1d72961eed789ddbe7213cb0d448fd59 100644 (file)
 static struct kmem_cache *lv2table_kmem_cache;
 
 static struct sysmmu_drvdata *sysmmu_drvdata_list;
+static struct exynos_iommu_owner *sysmmu_owner_list;
+
+struct sysmmu_list_data {
+       struct device *sysmmu;
+       struct list_head node;
+};
 
 struct exynos_client {
        struct list_head list;
@@ -247,6 +253,76 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *iommu_domain,
 static int exynos_iommu_of_xlate(struct device *master,
                                 struct of_phandle_args *spec)
 {
+       struct exynos_iommu_owner *owner = master->archdata.iommu;
+       struct platform_device *sysmmu_pdev = of_find_device_by_node(spec->np);
+       struct sysmmu_drvdata *data;
+       struct device *sysmmu;
+       struct exynos_client *client, *buf_client;
+       struct sysmmu_list_data *list_data;
+
+       if (!sysmmu_pdev)
+               return -ENODEV;
+
+       data = platform_get_drvdata(sysmmu_pdev);
+       if (!data)
+               return -ENODEV;
+
+       sysmmu = data->sysmmu;
+       if (!owner) {
+               owner = kzalloc(sizeof(*owner), GFP_KERNEL);
+               if (!owner)
+                       return -ENOMEM;
+
+               INIT_LIST_HEAD(&owner->sysmmu_list);
+               INIT_LIST_HEAD(&owner->client);
+               master->archdata.iommu = owner;
+               owner->master = master;
+               spin_lock_init(&owner->lock);
+
+               list_for_each_entry_safe(client, buf_client,
+                                       &exynos_client_list, list) {
+                       if (client->master_np == master->of_node) {
+                               owner->domain = client->vmm_data->domain;
+                               owner->vmm_data = client->vmm_data;
+                               list_del(&client->list);
+                               kfree(client);
+                       }
+               }
+
+               if (!sysmmu_owner_list) {
+                       sysmmu_owner_list = owner;
+               } else {
+                       owner->next = sysmmu_owner_list->next;
+                       sysmmu_owner_list->next = owner;
+               }
+       }
+
+       list_for_each_entry(list_data, &owner->sysmmu_list, node)
+               if (list_data->sysmmu == sysmmu)
+                       return 0;
+
+       list_data = devm_kzalloc(sysmmu, sizeof(*list_data), GFP_KERNEL);
+       if (!list_data)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&list_data->node);
+       list_data->sysmmu = sysmmu;
+
+       /*
+        * Use device link to make relationship between SysMMU and master.
+        * SysMMU device is supplier, and master device is consumer.
+        * This relationship guarantees that supplier is enabled before
+        * consumer, and it is disabled after consumer.
+        */
+       device_link_add(master, sysmmu, DL_FLAG_PM_RUNTIME);
+
+       /*
+        * System MMUs are attached in the order of the presence
+        * in device tree
+        */
+       list_add_tail(&list_data->node, &owner->sysmmu_list);
+       dev_info(master, "is owner of %s\n", dev_name(sysmmu));
+
        return 0;
 }
 
@@ -297,7 +373,6 @@ static int __init exynos_iommu_create_domain(void)
                                pr_info("Added client.%d[%s] into domain %s\n",
                                                i, np->name, domain_np->name);
                        }
-
                        of_node_put(np);
                }
                of_node_put(domain_np);