diff options
author | Jeffy Chen <jeffy.chen@rock-chips.com> | 2018-03-23 03:38:11 -0400 |
---|---|---|
committer | Joerg Roedel <jroedel@suse.de> | 2018-03-29 06:22:28 -0400 |
commit | 5fd577c3eac3bdb9aebfec01e9b3d7f07a14f2dd (patch) | |
tree | 5e79535085ecbbcdcce2d54da229f45eddc1467c | |
parent | 9176a303d971dc0fb35469c531c0d263667d2277 (diff) |
iommu/rockchip: Use OF_IOMMU to attach devices automatically
Converts the rockchip-iommu driver to use the OF_IOMMU infrastructure,
which allows attaching master devices to their IOMMUs automatically
according to DT properties.
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
-rw-r--r-- | drivers/iommu/rockchip-iommu.c | 135 |
1 files changed, 40 insertions, 95 deletions
diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c index 7970d21b9858..bd8580b897e9 100644 --- a/drivers/iommu/rockchip-iommu.c +++ b/drivers/iommu/rockchip-iommu.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/mm.h> | 19 | #include <linux/mm.h> |
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/of.h> | 21 | #include <linux/of.h> |
22 | #include <linux/of_iommu.h> | ||
22 | #include <linux/of_platform.h> | 23 | #include <linux/of_platform.h> |
23 | #include <linux/platform_device.h> | 24 | #include <linux/platform_device.h> |
24 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
@@ -104,6 +105,10 @@ struct rk_iommu { | |||
104 | struct iommu_domain *domain; /* domain to which iommu is attached */ | 105 | struct iommu_domain *domain; /* domain to which iommu is attached */ |
105 | }; | 106 | }; |
106 | 107 | ||
108 | struct rk_iommudata { | ||
109 | struct rk_iommu *iommu; | ||
110 | }; | ||
111 | |||
107 | static struct device *dma_dev; | 112 | static struct device *dma_dev; |
108 | 113 | ||
109 | static inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma, | 114 | static inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma, |
@@ -807,18 +812,9 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova, | |||
807 | 812 | ||
808 | static struct rk_iommu *rk_iommu_from_dev(struct device *dev) | 813 | static struct rk_iommu *rk_iommu_from_dev(struct device *dev) |
809 | { | 814 | { |
810 | struct iommu_group *group; | 815 | struct rk_iommudata *data = dev->archdata.iommu; |
811 | struct device *iommu_dev; | ||
812 | struct rk_iommu *rk_iommu; | ||
813 | 816 | ||
814 | group = iommu_group_get(dev); | 817 | return data ? data->iommu : NULL; |
815 | if (!group) | ||
816 | return NULL; | ||
817 | iommu_dev = iommu_group_get_iommudata(group); | ||
818 | rk_iommu = dev_get_drvdata(iommu_dev); | ||
819 | iommu_group_put(group); | ||
820 | |||
821 | return rk_iommu; | ||
822 | } | 818 | } |
823 | 819 | ||
824 | static int rk_iommu_attach_device(struct iommu_domain *domain, | 820 | static int rk_iommu_attach_device(struct iommu_domain *domain, |
@@ -989,110 +985,53 @@ static void rk_iommu_domain_free(struct iommu_domain *domain) | |||
989 | iommu_put_dma_cookie(&rk_domain->domain); | 985 | iommu_put_dma_cookie(&rk_domain->domain); |
990 | } | 986 | } |
991 | 987 | ||
992 | static bool rk_iommu_is_dev_iommu_master(struct device *dev) | 988 | static int rk_iommu_add_device(struct device *dev) |
993 | { | ||
994 | struct device_node *np = dev->of_node; | ||
995 | int ret; | ||
996 | |||
997 | /* | ||
998 | * An iommu master has an iommus property containing a list of phandles | ||
999 | * to iommu nodes, each with an #iommu-cells property with value 0. | ||
1000 | */ | ||
1001 | ret = of_count_phandle_with_args(np, "iommus", "#iommu-cells"); | ||
1002 | return (ret > 0); | ||
1003 | } | ||
1004 | |||
1005 | static int rk_iommu_group_set_iommudata(struct iommu_group *group, | ||
1006 | struct device *dev) | ||
1007 | { | 989 | { |
1008 | struct device_node *np = dev->of_node; | 990 | struct iommu_group *group; |
1009 | struct platform_device *pd; | 991 | struct rk_iommu *iommu; |
1010 | int ret; | ||
1011 | struct of_phandle_args args; | ||
1012 | 992 | ||
1013 | /* | 993 | iommu = rk_iommu_from_dev(dev); |
1014 | * An iommu master has an iommus property containing a list of phandles | 994 | if (!iommu) |
1015 | * to iommu nodes, each with an #iommu-cells property with value 0. | 995 | return -ENODEV; |
1016 | */ | ||
1017 | ret = of_parse_phandle_with_args(np, "iommus", "#iommu-cells", 0, | ||
1018 | &args); | ||
1019 | if (ret) { | ||
1020 | dev_err(dev, "of_parse_phandle_with_args(%pOF) => %d\n", | ||
1021 | np, ret); | ||
1022 | return ret; | ||
1023 | } | ||
1024 | if (args.args_count != 0) { | ||
1025 | dev_err(dev, "incorrect number of iommu params found for %pOF (found %d, expected 0)\n", | ||
1026 | args.np, args.args_count); | ||
1027 | return -EINVAL; | ||
1028 | } | ||
1029 | 996 | ||
1030 | pd = of_find_device_by_node(args.np); | 997 | group = iommu_group_get_for_dev(dev); |
1031 | of_node_put(args.np); | 998 | if (IS_ERR(group)) |
1032 | if (!pd) { | 999 | return PTR_ERR(group); |
1033 | dev_err(dev, "iommu %pOF not found\n", args.np); | 1000 | iommu_group_put(group); |
1034 | return -EPROBE_DEFER; | ||
1035 | } | ||
1036 | 1001 | ||
1037 | /* TODO(djkurtz): handle multiple slave iommus for a single master */ | 1002 | iommu_device_link(&iommu->iommu, dev); |
1038 | iommu_group_set_iommudata(group, &pd->dev, NULL); | ||
1039 | 1003 | ||
1040 | return 0; | 1004 | return 0; |
1041 | } | 1005 | } |
1042 | 1006 | ||
1043 | static int rk_iommu_add_device(struct device *dev) | 1007 | static void rk_iommu_remove_device(struct device *dev) |
1044 | { | 1008 | { |
1045 | struct iommu_group *group; | ||
1046 | struct rk_iommu *iommu; | 1009 | struct rk_iommu *iommu; |
1047 | int ret; | ||
1048 | |||
1049 | if (!rk_iommu_is_dev_iommu_master(dev)) | ||
1050 | return -ENODEV; | ||
1051 | |||
1052 | group = iommu_group_get(dev); | ||
1053 | if (!group) { | ||
1054 | group = iommu_group_alloc(); | ||
1055 | if (IS_ERR(group)) { | ||
1056 | dev_err(dev, "Failed to allocate IOMMU group\n"); | ||
1057 | return PTR_ERR(group); | ||
1058 | } | ||
1059 | } | ||
1060 | |||
1061 | ret = iommu_group_add_device(group, dev); | ||
1062 | if (ret) | ||
1063 | goto err_put_group; | ||
1064 | |||
1065 | ret = rk_iommu_group_set_iommudata(group, dev); | ||
1066 | if (ret) | ||
1067 | goto err_remove_device; | ||
1068 | 1010 | ||
1069 | iommu = rk_iommu_from_dev(dev); | 1011 | iommu = rk_iommu_from_dev(dev); |
1070 | if (iommu) | ||
1071 | iommu_device_link(&iommu->iommu, dev); | ||
1072 | 1012 | ||
1073 | iommu_group_put(group); | 1013 | iommu_device_unlink(&iommu->iommu, dev); |
1074 | |||
1075 | return 0; | ||
1076 | |||
1077 | err_remove_device: | ||
1078 | iommu_group_remove_device(dev); | 1014 | iommu_group_remove_device(dev); |
1079 | err_put_group: | ||
1080 | iommu_group_put(group); | ||
1081 | return ret; | ||
1082 | } | 1015 | } |
1083 | 1016 | ||
1084 | static void rk_iommu_remove_device(struct device *dev) | 1017 | static int rk_iommu_of_xlate(struct device *dev, |
1018 | struct of_phandle_args *args) | ||
1085 | { | 1019 | { |
1086 | struct rk_iommu *iommu; | 1020 | struct platform_device *iommu_dev; |
1021 | struct rk_iommudata *data; | ||
1087 | 1022 | ||
1088 | if (!rk_iommu_is_dev_iommu_master(dev)) | 1023 | data = devm_kzalloc(dma_dev, sizeof(*data), GFP_KERNEL); |
1089 | return; | 1024 | if (!data) |
1025 | return -ENOMEM; | ||
1090 | 1026 | ||
1091 | iommu = rk_iommu_from_dev(dev); | 1027 | iommu_dev = of_find_device_by_node(args->np); |
1092 | if (iommu) | ||
1093 | iommu_device_unlink(&iommu->iommu, dev); | ||
1094 | 1028 | ||
1095 | iommu_group_remove_device(dev); | 1029 | data->iommu = platform_get_drvdata(iommu_dev); |
1030 | dev->archdata.iommu = data; | ||
1031 | |||
1032 | of_dev_put(iommu_dev); | ||
1033 | |||
1034 | return 0; | ||
1096 | } | 1035 | } |
1097 | 1036 | ||
1098 | static const struct iommu_ops rk_iommu_ops = { | 1037 | static const struct iommu_ops rk_iommu_ops = { |
@@ -1106,7 +1045,9 @@ static const struct iommu_ops rk_iommu_ops = { | |||
1106 | .add_device = rk_iommu_add_device, | 1045 | .add_device = rk_iommu_add_device, |
1107 | .remove_device = rk_iommu_remove_device, | 1046 | .remove_device = rk_iommu_remove_device, |
1108 | .iova_to_phys = rk_iommu_iova_to_phys, | 1047 | .iova_to_phys = rk_iommu_iova_to_phys, |
1048 | .device_group = generic_device_group, | ||
1109 | .pgsize_bitmap = RK_IOMMU_PGSIZE_BITMAP, | 1049 | .pgsize_bitmap = RK_IOMMU_PGSIZE_BITMAP, |
1050 | .of_xlate = rk_iommu_of_xlate, | ||
1110 | }; | 1051 | }; |
1111 | 1052 | ||
1112 | static int rk_iommu_probe(struct platform_device *pdev) | 1053 | static int rk_iommu_probe(struct platform_device *pdev) |
@@ -1178,6 +1119,8 @@ static int rk_iommu_probe(struct platform_device *pdev) | |||
1178 | goto err_unprepare_clocks; | 1119 | goto err_unprepare_clocks; |
1179 | 1120 | ||
1180 | iommu_device_set_ops(&iommu->iommu, &rk_iommu_ops); | 1121 | iommu_device_set_ops(&iommu->iommu, &rk_iommu_ops); |
1122 | iommu_device_set_fwnode(&iommu->iommu, &dev->of_node->fwnode); | ||
1123 | |||
1181 | err = iommu_device_register(&iommu->iommu); | 1124 | err = iommu_device_register(&iommu->iommu); |
1182 | if (err) | 1125 | if (err) |
1183 | goto err_remove_sysfs; | 1126 | goto err_remove_sysfs; |
@@ -1250,6 +1193,8 @@ static int __init rk_iommu_init(void) | |||
1250 | } | 1193 | } |
1251 | subsys_initcall(rk_iommu_init); | 1194 | subsys_initcall(rk_iommu_init); |
1252 | 1195 | ||
1196 | IOMMU_OF_DECLARE(rk_iommu_of, "rockchip,iommu"); | ||
1197 | |||
1253 | MODULE_DESCRIPTION("IOMMU API for Rockchip"); | 1198 | MODULE_DESCRIPTION("IOMMU API for Rockchip"); |
1254 | MODULE_AUTHOR("Simon Xue <xxm@rock-chips.com> and Daniel Kurtz <djkurtz@chromium.org>"); | 1199 | MODULE_AUTHOR("Simon Xue <xxm@rock-chips.com> and Daniel Kurtz <djkurtz@chromium.org>"); |
1255 | MODULE_ALIAS("platform:rockchip-iommu"); | 1200 | MODULE_ALIAS("platform:rockchip-iommu"); |