diff options
Diffstat (limited to 'arch/arm/plat-omap/iommu.c')
-rw-r--r-- | arch/arm/plat-omap/iommu.c | 308 |
1 files changed, 266 insertions, 42 deletions
diff --git a/arch/arm/plat-omap/iommu.c b/arch/arm/plat-omap/iommu.c index 34fc31ee9081..51aa008d8223 100644 --- a/arch/arm/plat-omap/iommu.c +++ b/arch/arm/plat-omap/iommu.c | |||
@@ -18,6 +18,9 @@ | |||
18 | #include <linux/ioport.h> | 18 | #include <linux/ioport.h> |
19 | #include <linux/clk.h> | 19 | #include <linux/clk.h> |
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/iommu.h> | ||
22 | #include <linux/mutex.h> | ||
23 | #include <linux/spinlock.h> | ||
21 | 24 | ||
22 | #include <asm/cacheflush.h> | 25 | #include <asm/cacheflush.h> |
23 | 26 | ||
@@ -30,6 +33,19 @@ | |||
30 | (__i < (n)) && (cr = __iotlb_read_cr((obj), __i), true); \ | 33 | (__i < (n)) && (cr = __iotlb_read_cr((obj), __i), true); \ |
31 | __i++) | 34 | __i++) |
32 | 35 | ||
36 | /** | ||
37 | * struct omap_iommu_domain - omap iommu domain | ||
38 | * @pgtable: the page table | ||
39 | * @iommu_dev: an omap iommu device attached to this domain. only a single | ||
40 | * iommu device can be attached for now. | ||
41 | * @lock: domain lock, should be taken when attaching/detaching | ||
42 | */ | ||
43 | struct omap_iommu_domain { | ||
44 | u32 *pgtable; | ||
45 | struct iommu *iommu_dev; | ||
46 | spinlock_t lock; | ||
47 | }; | ||
48 | |||
33 | /* accommodate the difference between omap1 and omap2/3 */ | 49 | /* accommodate the difference between omap1 and omap2/3 */ |
34 | static const struct iommu_functions *arch_iommu; | 50 | static const struct iommu_functions *arch_iommu; |
35 | 51 | ||
@@ -852,35 +868,55 @@ int iommu_set_da_range(struct iommu *obj, u32 start, u32 end) | |||
852 | EXPORT_SYMBOL_GPL(iommu_set_da_range); | 868 | EXPORT_SYMBOL_GPL(iommu_set_da_range); |
853 | 869 | ||
854 | /** | 870 | /** |
855 | * iommu_get - Get iommu handler | 871 | * omap_find_iommu_device() - find an omap iommu device by name |
856 | * @name: target iommu name | 872 | * @name: name of the iommu device |
873 | * | ||
874 | * The generic iommu API requires the caller to provide the device | ||
875 | * he wishes to attach to a certain iommu domain. | ||
876 | * | ||
877 | * Drivers generally should not bother with this as it should just | ||
878 | * be taken care of by the DMA-API using dev_archdata. | ||
879 | * | ||
880 | * This function is provided as an interim solution until the latter | ||
881 | * materializes, and omap3isp is fully migrated to the DMA-API. | ||
882 | */ | ||
883 | struct device *omap_find_iommu_device(const char *name) | ||
884 | { | ||
885 | return driver_find_device(&omap_iommu_driver.driver, NULL, | ||
886 | (void *)name, | ||
887 | device_match_by_alias); | ||
888 | } | ||
889 | EXPORT_SYMBOL_GPL(omap_find_iommu_device); | ||
890 | |||
891 | /** | ||
892 | * omap_iommu_attach() - attach iommu device to an iommu domain | ||
893 | * @dev: target omap iommu device | ||
894 | * @iopgd: page table | ||
857 | **/ | 895 | **/ |
858 | struct iommu *iommu_get(const char *name) | 896 | static struct iommu *omap_iommu_attach(struct device *dev, u32 *iopgd) |
859 | { | 897 | { |
860 | int err = -ENOMEM; | 898 | int err = -ENOMEM; |
861 | struct device *dev; | 899 | struct iommu *obj = to_iommu(dev); |
862 | struct iommu *obj; | ||
863 | |||
864 | dev = driver_find_device(&omap_iommu_driver.driver, NULL, (void *)name, | ||
865 | device_match_by_alias); | ||
866 | if (!dev) | ||
867 | return ERR_PTR(-ENODEV); | ||
868 | |||
869 | obj = to_iommu(dev); | ||
870 | 900 | ||
871 | mutex_lock(&obj->iommu_lock); | 901 | spin_lock(&obj->iommu_lock); |
872 | 902 | ||
873 | if (obj->refcount++ == 0) { | 903 | /* an iommu device can only be attached once */ |
874 | err = iommu_enable(obj); | 904 | if (++obj->refcount > 1) { |
875 | if (err) | 905 | dev_err(dev, "%s: already attached!\n", obj->name); |
876 | goto err_enable; | 906 | err = -EBUSY; |
877 | flush_iotlb_all(obj); | 907 | goto err_enable; |
878 | } | 908 | } |
879 | 909 | ||
910 | obj->iopgd = iopgd; | ||
911 | err = iommu_enable(obj); | ||
912 | if (err) | ||
913 | goto err_enable; | ||
914 | flush_iotlb_all(obj); | ||
915 | |||
880 | if (!try_module_get(obj->owner)) | 916 | if (!try_module_get(obj->owner)) |
881 | goto err_module; | 917 | goto err_module; |
882 | 918 | ||
883 | mutex_unlock(&obj->iommu_lock); | 919 | spin_unlock(&obj->iommu_lock); |
884 | 920 | ||
885 | dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name); | 921 | dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name); |
886 | return obj; | 922 | return obj; |
@@ -890,32 +926,32 @@ err_module: | |||
890 | iommu_disable(obj); | 926 | iommu_disable(obj); |
891 | err_enable: | 927 | err_enable: |
892 | obj->refcount--; | 928 | obj->refcount--; |
893 | mutex_unlock(&obj->iommu_lock); | 929 | spin_unlock(&obj->iommu_lock); |
894 | return ERR_PTR(err); | 930 | return ERR_PTR(err); |
895 | } | 931 | } |
896 | EXPORT_SYMBOL_GPL(iommu_get); | ||
897 | 932 | ||
898 | /** | 933 | /** |
899 | * iommu_put - Put back iommu handler | 934 | * omap_iommu_detach - release iommu device |
900 | * @obj: target iommu | 935 | * @obj: target iommu |
901 | **/ | 936 | **/ |
902 | void iommu_put(struct iommu *obj) | 937 | static void omap_iommu_detach(struct iommu *obj) |
903 | { | 938 | { |
904 | if (!obj || IS_ERR(obj)) | 939 | if (!obj || IS_ERR(obj)) |
905 | return; | 940 | return; |
906 | 941 | ||
907 | mutex_lock(&obj->iommu_lock); | 942 | spin_lock(&obj->iommu_lock); |
908 | 943 | ||
909 | if (--obj->refcount == 0) | 944 | if (--obj->refcount == 0) |
910 | iommu_disable(obj); | 945 | iommu_disable(obj); |
911 | 946 | ||
912 | module_put(obj->owner); | 947 | module_put(obj->owner); |
913 | 948 | ||
914 | mutex_unlock(&obj->iommu_lock); | 949 | obj->iopgd = NULL; |
950 | |||
951 | spin_unlock(&obj->iommu_lock); | ||
915 | 952 | ||
916 | dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name); | 953 | dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name); |
917 | } | 954 | } |
918 | EXPORT_SYMBOL_GPL(iommu_put); | ||
919 | 955 | ||
920 | int iommu_set_isr(const char *name, | 956 | int iommu_set_isr(const char *name, |
921 | int (*isr)(struct iommu *obj, u32 da, u32 iommu_errs, | 957 | int (*isr)(struct iommu *obj, u32 da, u32 iommu_errs, |
@@ -950,7 +986,6 @@ EXPORT_SYMBOL_GPL(iommu_set_isr); | |||
950 | static int __devinit omap_iommu_probe(struct platform_device *pdev) | 986 | static int __devinit omap_iommu_probe(struct platform_device *pdev) |
951 | { | 987 | { |
952 | int err = -ENODEV; | 988 | int err = -ENODEV; |
953 | void *p; | ||
954 | int irq; | 989 | int irq; |
955 | struct iommu *obj; | 990 | struct iommu *obj; |
956 | struct resource *res; | 991 | struct resource *res; |
@@ -974,7 +1009,7 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev) | |||
974 | obj->da_start = pdata->da_start; | 1009 | obj->da_start = pdata->da_start; |
975 | obj->da_end = pdata->da_end; | 1010 | obj->da_end = pdata->da_end; |
976 | 1011 | ||
977 | mutex_init(&obj->iommu_lock); | 1012 | spin_lock_init(&obj->iommu_lock); |
978 | mutex_init(&obj->mmap_lock); | 1013 | mutex_init(&obj->mmap_lock); |
979 | spin_lock_init(&obj->page_table_lock); | 1014 | spin_lock_init(&obj->page_table_lock); |
980 | INIT_LIST_HEAD(&obj->mmap); | 1015 | INIT_LIST_HEAD(&obj->mmap); |
@@ -1009,22 +1044,9 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev) | |||
1009 | goto err_irq; | 1044 | goto err_irq; |
1010 | platform_set_drvdata(pdev, obj); | 1045 | platform_set_drvdata(pdev, obj); |
1011 | 1046 | ||
1012 | p = (void *)__get_free_pages(GFP_KERNEL, get_order(IOPGD_TABLE_SIZE)); | ||
1013 | if (!p) { | ||
1014 | err = -ENOMEM; | ||
1015 | goto err_pgd; | ||
1016 | } | ||
1017 | memset(p, 0, IOPGD_TABLE_SIZE); | ||
1018 | clean_dcache_area(p, IOPGD_TABLE_SIZE); | ||
1019 | obj->iopgd = p; | ||
1020 | |||
1021 | BUG_ON(!IS_ALIGNED((unsigned long)obj->iopgd, IOPGD_TABLE_SIZE)); | ||
1022 | |||
1023 | dev_info(&pdev->dev, "%s registered\n", obj->name); | 1047 | dev_info(&pdev->dev, "%s registered\n", obj->name); |
1024 | return 0; | 1048 | return 0; |
1025 | 1049 | ||
1026 | err_pgd: | ||
1027 | free_irq(irq, obj); | ||
1028 | err_irq: | 1050 | err_irq: |
1029 | iounmap(obj->regbase); | 1051 | iounmap(obj->regbase); |
1030 | err_ioremap: | 1052 | err_ioremap: |
@@ -1045,7 +1067,6 @@ static int __devexit omap_iommu_remove(struct platform_device *pdev) | |||
1045 | platform_set_drvdata(pdev, NULL); | 1067 | platform_set_drvdata(pdev, NULL); |
1046 | 1068 | ||
1047 | iopgtable_clear_entry_all(obj); | 1069 | iopgtable_clear_entry_all(obj); |
1048 | free_pages((unsigned long)obj->iopgd, get_order(IOPGD_TABLE_SIZE)); | ||
1049 | 1070 | ||
1050 | irq = platform_get_irq(pdev, 0); | 1071 | irq = platform_get_irq(pdev, 0); |
1051 | free_irq(irq, obj); | 1072 | free_irq(irq, obj); |
@@ -1072,6 +1093,207 @@ static void iopte_cachep_ctor(void *iopte) | |||
1072 | clean_dcache_area(iopte, IOPTE_TABLE_SIZE); | 1093 | clean_dcache_area(iopte, IOPTE_TABLE_SIZE); |
1073 | } | 1094 | } |
1074 | 1095 | ||
1096 | static int omap_iommu_map(struct iommu_domain *domain, unsigned long da, | ||
1097 | phys_addr_t pa, int order, int prot) | ||
1098 | { | ||
1099 | struct omap_iommu_domain *omap_domain = domain->priv; | ||
1100 | struct iommu *oiommu = omap_domain->iommu_dev; | ||
1101 | struct device *dev = oiommu->dev; | ||
1102 | size_t bytes = PAGE_SIZE << order; | ||
1103 | struct iotlb_entry e; | ||
1104 | int omap_pgsz; | ||
1105 | u32 ret, flags; | ||
1106 | |||
1107 | /* we only support mapping a single iommu page for now */ | ||
1108 | omap_pgsz = bytes_to_iopgsz(bytes); | ||
1109 | if (omap_pgsz < 0) { | ||
1110 | dev_err(dev, "invalid size to map: %d\n", bytes); | ||
1111 | return -EINVAL; | ||
1112 | } | ||
1113 | |||
1114 | dev_dbg(dev, "mapping da 0x%lx to pa 0x%x size 0x%x\n", da, pa, bytes); | ||
1115 | |||
1116 | flags = omap_pgsz | prot; | ||
1117 | |||
1118 | iotlb_init_entry(&e, da, pa, flags); | ||
1119 | |||
1120 | ret = iopgtable_store_entry(oiommu, &e); | ||
1121 | if (ret) { | ||
1122 | dev_err(dev, "iopgtable_store_entry failed: %d\n", ret); | ||
1123 | return ret; | ||
1124 | } | ||
1125 | |||
1126 | return 0; | ||
1127 | } | ||
1128 | |||
1129 | static int omap_iommu_unmap(struct iommu_domain *domain, unsigned long da, | ||
1130 | int order) | ||
1131 | { | ||
1132 | struct omap_iommu_domain *omap_domain = domain->priv; | ||
1133 | struct iommu *oiommu = omap_domain->iommu_dev; | ||
1134 | struct device *dev = oiommu->dev; | ||
1135 | size_t bytes = PAGE_SIZE << order; | ||
1136 | size_t ret; | ||
1137 | |||
1138 | dev_dbg(dev, "unmapping da 0x%lx size 0x%x\n", da, bytes); | ||
1139 | |||
1140 | ret = iopgtable_clear_entry(oiommu, da); | ||
1141 | if (ret != bytes) { | ||
1142 | dev_err(dev, "entry @ 0x%lx was %d; not %d\n", da, ret, bytes); | ||
1143 | return -EINVAL; | ||
1144 | } | ||
1145 | |||
1146 | return 0; | ||
1147 | } | ||
1148 | |||
1149 | static int | ||
1150 | omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) | ||
1151 | { | ||
1152 | struct omap_iommu_domain *omap_domain = domain->priv; | ||
1153 | struct iommu *oiommu; | ||
1154 | int ret = 0; | ||
1155 | |||
1156 | spin_lock(&omap_domain->lock); | ||
1157 | |||
1158 | /* only a single device is supported per domain for now */ | ||
1159 | if (omap_domain->iommu_dev) { | ||
1160 | dev_err(dev, "iommu domain is already attached\n"); | ||
1161 | ret = -EBUSY; | ||
1162 | goto out; | ||
1163 | } | ||
1164 | |||
1165 | /* get a handle to and enable the omap iommu */ | ||
1166 | oiommu = omap_iommu_attach(dev, omap_domain->pgtable); | ||
1167 | if (IS_ERR(oiommu)) { | ||
1168 | ret = PTR_ERR(oiommu); | ||
1169 | dev_err(dev, "can't get omap iommu: %d\n", ret); | ||
1170 | goto out; | ||
1171 | } | ||
1172 | |||
1173 | omap_domain->iommu_dev = oiommu; | ||
1174 | |||
1175 | out: | ||
1176 | spin_unlock(&omap_domain->lock); | ||
1177 | return ret; | ||
1178 | } | ||
1179 | |||
1180 | static void omap_iommu_detach_dev(struct iommu_domain *domain, | ||
1181 | struct device *dev) | ||
1182 | { | ||
1183 | struct omap_iommu_domain *omap_domain = domain->priv; | ||
1184 | struct iommu *oiommu = to_iommu(dev); | ||
1185 | |||
1186 | spin_lock(&omap_domain->lock); | ||
1187 | |||
1188 | /* only a single device is supported per domain for now */ | ||
1189 | if (omap_domain->iommu_dev != oiommu) { | ||
1190 | dev_err(dev, "invalid iommu device\n"); | ||
1191 | goto out; | ||
1192 | } | ||
1193 | |||
1194 | iopgtable_clear_entry_all(oiommu); | ||
1195 | |||
1196 | omap_iommu_detach(oiommu); | ||
1197 | |||
1198 | omap_domain->iommu_dev = NULL; | ||
1199 | |||
1200 | out: | ||
1201 | spin_unlock(&omap_domain->lock); | ||
1202 | } | ||
1203 | |||
1204 | static int omap_iommu_domain_init(struct iommu_domain *domain) | ||
1205 | { | ||
1206 | struct omap_iommu_domain *omap_domain; | ||
1207 | |||
1208 | omap_domain = kzalloc(sizeof(*omap_domain), GFP_KERNEL); | ||
1209 | if (!omap_domain) { | ||
1210 | pr_err("kzalloc failed\n"); | ||
1211 | goto out; | ||
1212 | } | ||
1213 | |||
1214 | omap_domain->pgtable = kzalloc(IOPGD_TABLE_SIZE, GFP_KERNEL); | ||
1215 | if (!omap_domain->pgtable) { | ||
1216 | pr_err("kzalloc failed\n"); | ||
1217 | goto fail_nomem; | ||
1218 | } | ||
1219 | |||
1220 | /* | ||
1221 | * should never fail, but please keep this around to ensure | ||
1222 | * we keep the hardware happy | ||
1223 | */ | ||
1224 | BUG_ON(!IS_ALIGNED((long)omap_domain->pgtable, IOPGD_TABLE_SIZE)); | ||
1225 | |||
1226 | clean_dcache_area(omap_domain->pgtable, IOPGD_TABLE_SIZE); | ||
1227 | spin_lock_init(&omap_domain->lock); | ||
1228 | |||
1229 | domain->priv = omap_domain; | ||
1230 | |||
1231 | return 0; | ||
1232 | |||
1233 | fail_nomem: | ||
1234 | kfree(omap_domain); | ||
1235 | out: | ||
1236 | return -ENOMEM; | ||
1237 | } | ||
1238 | |||
1239 | /* assume device was already detached */ | ||
1240 | static void omap_iommu_domain_destroy(struct iommu_domain *domain) | ||
1241 | { | ||
1242 | struct omap_iommu_domain *omap_domain = domain->priv; | ||
1243 | |||
1244 | domain->priv = NULL; | ||
1245 | |||
1246 | kfree(omap_domain->pgtable); | ||
1247 | kfree(omap_domain); | ||
1248 | } | ||
1249 | |||
1250 | static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain, | ||
1251 | unsigned long da) | ||
1252 | { | ||
1253 | struct omap_iommu_domain *omap_domain = domain->priv; | ||
1254 | struct iommu *oiommu = omap_domain->iommu_dev; | ||
1255 | struct device *dev = oiommu->dev; | ||
1256 | u32 *pgd, *pte; | ||
1257 | phys_addr_t ret = 0; | ||
1258 | |||
1259 | iopgtable_lookup_entry(oiommu, da, &pgd, &pte); | ||
1260 | |||
1261 | if (pte) { | ||
1262 | if (iopte_is_small(*pte)) | ||
1263 | ret = omap_iommu_translate(*pte, da, IOPTE_MASK); | ||
1264 | else if (iopte_is_large(*pte)) | ||
1265 | ret = omap_iommu_translate(*pte, da, IOLARGE_MASK); | ||
1266 | else | ||
1267 | dev_err(dev, "bogus pte 0x%x", *pte); | ||
1268 | } else { | ||
1269 | if (iopgd_is_section(*pgd)) | ||
1270 | ret = omap_iommu_translate(*pgd, da, IOSECTION_MASK); | ||
1271 | else if (iopgd_is_super(*pgd)) | ||
1272 | ret = omap_iommu_translate(*pgd, da, IOSUPER_MASK); | ||
1273 | else | ||
1274 | dev_err(dev, "bogus pgd 0x%x", *pgd); | ||
1275 | } | ||
1276 | |||
1277 | return ret; | ||
1278 | } | ||
1279 | |||
1280 | static int omap_iommu_domain_has_cap(struct iommu_domain *domain, | ||
1281 | unsigned long cap) | ||
1282 | { | ||
1283 | return 0; | ||
1284 | } | ||
1285 | |||
1286 | static struct iommu_ops omap_iommu_ops = { | ||
1287 | .domain_init = omap_iommu_domain_init, | ||
1288 | .domain_destroy = omap_iommu_domain_destroy, | ||
1289 | .attach_dev = omap_iommu_attach_dev, | ||
1290 | .detach_dev = omap_iommu_detach_dev, | ||
1291 | .map = omap_iommu_map, | ||
1292 | .unmap = omap_iommu_unmap, | ||
1293 | .iova_to_phys = omap_iommu_iova_to_phys, | ||
1294 | .domain_has_cap = omap_iommu_domain_has_cap, | ||
1295 | }; | ||
1296 | |||
1075 | static int __init omap_iommu_init(void) | 1297 | static int __init omap_iommu_init(void) |
1076 | { | 1298 | { |
1077 | struct kmem_cache *p; | 1299 | struct kmem_cache *p; |
@@ -1084,6 +1306,8 @@ static int __init omap_iommu_init(void) | |||
1084 | return -ENOMEM; | 1306 | return -ENOMEM; |
1085 | iopte_cachep = p; | 1307 | iopte_cachep = p; |
1086 | 1308 | ||
1309 | register_iommu(&omap_iommu_ops); | ||
1310 | |||
1087 | return platform_driver_register(&omap_iommu_driver); | 1311 | return platform_driver_register(&omap_iommu_driver); |
1088 | } | 1312 | } |
1089 | module_init(omap_iommu_init); | 1313 | module_init(omap_iommu_init); |