diff options
-rw-r--r-- | Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt | 21 | ||||
-rw-r--r-- | drivers/iommu/Kconfig | 2 | ||||
-rw-r--r-- | drivers/iommu/tegra-smmu.c | 149 |
3 files changed, 117 insertions, 55 deletions
diff --git a/Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt b/Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt new file mode 100644 index 000000000000..89fb5434b730 --- /dev/null +++ b/Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt | |||
@@ -0,0 +1,21 @@ | |||
1 | NVIDIA Tegra 30 IOMMU H/W, SMMU (System Memory Management Unit) | ||
2 | |||
3 | Required properties: | ||
4 | - compatible : "nvidia,tegra30-smmu" | ||
5 | - reg : Should contain 3 register banks(address and length) for each | ||
6 | of the SMMU register blocks. | ||
7 | - interrupts : Should contain MC General interrupt. | ||
8 | - nvidia,#asids : # of ASIDs | ||
9 | - dma-window : IOVA start address and length. | ||
10 | - nvidia,ahb : phandle to the ahb bus connected to SMMU. | ||
11 | |||
12 | Example: | ||
13 | smmu { | ||
14 | compatible = "nvidia,tegra30-smmu"; | ||
15 | reg = <0x7000f010 0x02c | ||
16 | 0x7000f1f0 0x010 | ||
17 | 0x7000f228 0x05c>; | ||
18 | nvidia,#asids = <4>; /* # of ASIDs */ | ||
19 | dma-window = <0 0x40000000>; /* IOVA start & length */ | ||
20 | nvidia,ahb = <&ahb>; | ||
21 | }; | ||
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 4826af62a9de..9f69b561f5db 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig | |||
@@ -158,7 +158,7 @@ config TEGRA_IOMMU_GART | |||
158 | 158 | ||
159 | config TEGRA_IOMMU_SMMU | 159 | config TEGRA_IOMMU_SMMU |
160 | bool "Tegra SMMU IOMMU Support" | 160 | bool "Tegra SMMU IOMMU Support" |
161 | depends on ARCH_TEGRA_3x_SOC | 161 | depends on ARCH_TEGRA_3x_SOC && TEGRA_AHB |
162 | select IOMMU_API | 162 | select IOMMU_API |
163 | help | 163 | help |
164 | Enables support for remapping discontiguous physical memory | 164 | Enables support for remapping discontiguous physical memory |
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index ecd679043d77..2c92b8c3514e 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c | |||
@@ -30,12 +30,15 @@ | |||
30 | #include <linux/sched.h> | 30 | #include <linux/sched.h> |
31 | #include <linux/iommu.h> | 31 | #include <linux/iommu.h> |
32 | #include <linux/io.h> | 32 | #include <linux/io.h> |
33 | #include <linux/of.h> | ||
34 | #include <linux/of_iommu.h> | ||
33 | 35 | ||
34 | #include <asm/page.h> | 36 | #include <asm/page.h> |
35 | #include <asm/cacheflush.h> | 37 | #include <asm/cacheflush.h> |
36 | 38 | ||
37 | #include <mach/iomap.h> | 39 | #include <mach/iomap.h> |
38 | #include <mach/smmu.h> | 40 | #include <mach/smmu.h> |
41 | #include <mach/tegra-ahb.h> | ||
39 | 42 | ||
40 | /* bitmap of the page sizes currently supported */ | 43 | /* bitmap of the page sizes currently supported */ |
41 | #define SMMU_IOMMU_PGSIZES (SZ_4K) | 44 | #define SMMU_IOMMU_PGSIZES (SZ_4K) |
@@ -111,12 +114,6 @@ | |||
111 | 114 | ||
112 | #define SMMU_PDE_NEXT_SHIFT 28 | 115 | #define SMMU_PDE_NEXT_SHIFT 28 |
113 | 116 | ||
114 | /* AHB Arbiter Registers */ | ||
115 | #define AHB_XBAR_CTRL 0xe0 | ||
116 | #define AHB_XBAR_CTRL_SMMU_INIT_DONE_DONE 1 | ||
117 | #define AHB_XBAR_CTRL_SMMU_INIT_DONE_SHIFT 17 | ||
118 | |||
119 | #define SMMU_NUM_ASIDS 4 | ||
120 | #define SMMU_TLB_FLUSH_VA_SECTION__MASK 0xffc00000 | 117 | #define SMMU_TLB_FLUSH_VA_SECTION__MASK 0xffc00000 |
121 | #define SMMU_TLB_FLUSH_VA_SECTION__SHIFT 12 /* right shift */ | 118 | #define SMMU_TLB_FLUSH_VA_SECTION__SHIFT 12 /* right shift */ |
122 | #define SMMU_TLB_FLUSH_VA_GROUP__MASK 0xffffc000 | 119 | #define SMMU_TLB_FLUSH_VA_GROUP__MASK 0xffffc000 |
@@ -136,6 +133,7 @@ | |||
136 | 133 | ||
137 | #define SMMU_PAGE_SHIFT 12 | 134 | #define SMMU_PAGE_SHIFT 12 |
138 | #define SMMU_PAGE_SIZE (1 << SMMU_PAGE_SHIFT) | 135 | #define SMMU_PAGE_SIZE (1 << SMMU_PAGE_SHIFT) |
136 | #define SMMU_PAGE_MASK ((1 << SMMU_PAGE_SHIFT) - 1) | ||
139 | 137 | ||
140 | #define SMMU_PDIR_COUNT 1024 | 138 | #define SMMU_PDIR_COUNT 1024 |
141 | #define SMMU_PDIR_SIZE (sizeof(unsigned long) * SMMU_PDIR_COUNT) | 139 | #define SMMU_PDIR_SIZE (sizeof(unsigned long) * SMMU_PDIR_COUNT) |
@@ -177,6 +175,8 @@ | |||
177 | #define SMMU_ASID_DISABLE 0 | 175 | #define SMMU_ASID_DISABLE 0 |
178 | #define SMMU_ASID_ASID(n) ((n) & ~SMMU_ASID_ENABLE(0)) | 176 | #define SMMU_ASID_ASID(n) ((n) & ~SMMU_ASID_ENABLE(0)) |
179 | 177 | ||
178 | #define NUM_SMMU_REG_BANKS 3 | ||
179 | |||
180 | #define smmu_client_enable_hwgrp(c, m) smmu_client_set_hwgrp(c, m, 1) | 180 | #define smmu_client_enable_hwgrp(c, m) smmu_client_set_hwgrp(c, m, 1) |
181 | #define smmu_client_disable_hwgrp(c) smmu_client_set_hwgrp(c, 0, 0) | 181 | #define smmu_client_disable_hwgrp(c) smmu_client_set_hwgrp(c, 0, 0) |
182 | #define __smmu_client_enable_hwgrp(c, m) __smmu_client_set_hwgrp(c, m, 1) | 182 | #define __smmu_client_enable_hwgrp(c, m) __smmu_client_set_hwgrp(c, m, 1) |
@@ -235,7 +235,7 @@ struct smmu_as { | |||
235 | * Per SMMU device - IOMMU device | 235 | * Per SMMU device - IOMMU device |
236 | */ | 236 | */ |
237 | struct smmu_device { | 237 | struct smmu_device { |
238 | void __iomem *regs, *regs_ahbarb; | 238 | void __iomem *regs[NUM_SMMU_REG_BANKS]; |
239 | unsigned long iovmm_base; /* remappable base address */ | 239 | unsigned long iovmm_base; /* remappable base address */ |
240 | unsigned long page_count; /* total remappable size */ | 240 | unsigned long page_count; /* total remappable size */ |
241 | spinlock_t lock; | 241 | spinlock_t lock; |
@@ -252,29 +252,47 @@ struct smmu_device { | |||
252 | unsigned long translation_enable_1; | 252 | unsigned long translation_enable_1; |
253 | unsigned long translation_enable_2; | 253 | unsigned long translation_enable_2; |
254 | unsigned long asid_security; | 254 | unsigned long asid_security; |
255 | |||
256 | struct device_node *ahb; | ||
255 | }; | 257 | }; |
256 | 258 | ||
257 | static struct smmu_device *smmu_handle; /* unique for a system */ | 259 | static struct smmu_device *smmu_handle; /* unique for a system */ |
258 | 260 | ||
259 | /* | 261 | /* |
260 | * SMMU/AHB register accessors | 262 | * SMMU register accessors |
261 | */ | 263 | */ |
262 | static inline u32 smmu_read(struct smmu_device *smmu, size_t offs) | 264 | static inline u32 smmu_read(struct smmu_device *smmu, size_t offs) |
263 | { | 265 | { |
264 | return readl(smmu->regs + offs); | 266 | BUG_ON(offs < 0x10); |
265 | } | 267 | if (offs < 0x3c) |
266 | static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs) | 268 | return readl(smmu->regs[0] + offs - 0x10); |
267 | { | 269 | BUG_ON(offs < 0x1f0); |
268 | writel(val, smmu->regs + offs); | 270 | if (offs < 0x200) |
271 | return readl(smmu->regs[1] + offs - 0x1f0); | ||
272 | BUG_ON(offs < 0x228); | ||
273 | if (offs < 0x284) | ||
274 | return readl(smmu->regs[2] + offs - 0x228); | ||
275 | BUG(); | ||
269 | } | 276 | } |
270 | 277 | ||
271 | static inline u32 ahb_read(struct smmu_device *smmu, size_t offs) | 278 | static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs) |
272 | { | ||
273 | return readl(smmu->regs_ahbarb + offs); | ||
274 | } | ||
275 | static inline void ahb_write(struct smmu_device *smmu, u32 val, size_t offs) | ||
276 | { | 279 | { |
277 | writel(val, smmu->regs_ahbarb + offs); | 280 | BUG_ON(offs < 0x10); |
281 | if (offs < 0x3c) { | ||
282 | writel(val, smmu->regs[0] + offs - 0x10); | ||
283 | return; | ||
284 | } | ||
285 | BUG_ON(offs < 0x1f0); | ||
286 | if (offs < 0x200) { | ||
287 | writel(val, smmu->regs[1] + offs - 0x1f0); | ||
288 | return; | ||
289 | } | ||
290 | BUG_ON(offs < 0x228); | ||
291 | if (offs < 0x284) { | ||
292 | writel(val, smmu->regs[2] + offs - 0x228); | ||
293 | return; | ||
294 | } | ||
295 | BUG(); | ||
278 | } | 296 | } |
279 | 297 | ||
280 | #define VA_PAGE_TO_PA(va, page) \ | 298 | #define VA_PAGE_TO_PA(va, page) \ |
@@ -370,7 +388,7 @@ static void smmu_flush_regs(struct smmu_device *smmu, int enable) | |||
370 | FLUSH_SMMU_REGS(smmu); | 388 | FLUSH_SMMU_REGS(smmu); |
371 | } | 389 | } |
372 | 390 | ||
373 | static void smmu_setup_regs(struct smmu_device *smmu) | 391 | static int smmu_setup_regs(struct smmu_device *smmu) |
374 | { | 392 | { |
375 | int i; | 393 | int i; |
376 | u32 val; | 394 | u32 val; |
@@ -398,10 +416,7 @@ static void smmu_setup_regs(struct smmu_device *smmu) | |||
398 | 416 | ||
399 | smmu_flush_regs(smmu, 1); | 417 | smmu_flush_regs(smmu, 1); |
400 | 418 | ||
401 | val = ahb_read(smmu, AHB_XBAR_CTRL); | 419 | return tegra_ahb_enable_smmu(smmu->ahb); |
402 | val |= AHB_XBAR_CTRL_SMMU_INIT_DONE_DONE << | ||
403 | AHB_XBAR_CTRL_SMMU_INIT_DONE_SHIFT; | ||
404 | ahb_write(smmu, val, AHB_XBAR_CTRL); | ||
405 | } | 420 | } |
406 | 421 | ||
407 | static void flush_ptc_and_tlb(struct smmu_device *smmu, | 422 | static void flush_ptc_and_tlb(struct smmu_device *smmu, |
@@ -873,52 +888,72 @@ static int tegra_smmu_resume(struct device *dev) | |||
873 | { | 888 | { |
874 | struct smmu_device *smmu = dev_get_drvdata(dev); | 889 | struct smmu_device *smmu = dev_get_drvdata(dev); |
875 | unsigned long flags; | 890 | unsigned long flags; |
891 | int err; | ||
876 | 892 | ||
877 | spin_lock_irqsave(&smmu->lock, flags); | 893 | spin_lock_irqsave(&smmu->lock, flags); |
878 | smmu_setup_regs(smmu); | 894 | err = smmu_setup_regs(smmu); |
879 | spin_unlock_irqrestore(&smmu->lock, flags); | 895 | spin_unlock_irqrestore(&smmu->lock, flags); |
880 | return 0; | 896 | return err; |
881 | } | 897 | } |
882 | 898 | ||
883 | static int tegra_smmu_probe(struct platform_device *pdev) | 899 | static int tegra_smmu_probe(struct platform_device *pdev) |
884 | { | 900 | { |
885 | struct smmu_device *smmu; | 901 | struct smmu_device *smmu; |
886 | struct resource *regs, *regs2, *window; | ||
887 | struct device *dev = &pdev->dev; | 902 | struct device *dev = &pdev->dev; |
888 | int i, err = 0; | 903 | int i, asids, err = 0; |
904 | dma_addr_t base; | ||
905 | size_t size; | ||
906 | const void *prop; | ||
889 | 907 | ||
890 | if (smmu_handle) | 908 | if (smmu_handle) |
891 | return -EIO; | 909 | return -EIO; |
892 | 910 | ||
893 | BUILD_BUG_ON(PAGE_SHIFT != SMMU_PAGE_SHIFT); | 911 | BUILD_BUG_ON(PAGE_SHIFT != SMMU_PAGE_SHIFT); |
894 | 912 | ||
895 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
896 | regs2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
897 | window = platform_get_resource(pdev, IORESOURCE_MEM, 2); | ||
898 | if (!regs || !regs2 || !window) { | ||
899 | dev_err(dev, "No SMMU resources\n"); | ||
900 | return -ENODEV; | ||
901 | } | ||
902 | |||
903 | smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); | 913 | smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); |
904 | if (!smmu) { | 914 | if (!smmu) { |
905 | dev_err(dev, "failed to allocate smmu_device\n"); | 915 | dev_err(dev, "failed to allocate smmu_device\n"); |
906 | return -ENOMEM; | 916 | return -ENOMEM; |
907 | } | 917 | } |
908 | 918 | ||
909 | smmu->dev = dev; | 919 | for (i = 0; i < ARRAY_SIZE(smmu->regs); i++) { |
910 | smmu->num_as = SMMU_NUM_ASIDS; | 920 | struct resource *res; |
911 | smmu->iovmm_base = (unsigned long)window->start; | 921 | |
912 | smmu->page_count = resource_size(window) >> SMMU_PAGE_SHIFT; | 922 | res = platform_get_resource(pdev, IORESOURCE_MEM, i); |
913 | smmu->regs = devm_ioremap(dev, regs->start, resource_size(regs)); | 923 | if (!res) |
914 | smmu->regs_ahbarb = devm_ioremap(dev, regs2->start, | 924 | return -ENODEV; |
915 | resource_size(regs2)); | 925 | smmu->regs[i] = devm_request_and_ioremap(&pdev->dev, res); |
916 | if (!smmu->regs || !smmu->regs_ahbarb) { | 926 | if (!smmu->regs[i]) |
917 | dev_err(dev, "failed to remap SMMU registers\n"); | 927 | return -EBUSY; |
918 | err = -ENXIO; | ||
919 | goto fail; | ||
920 | } | 928 | } |
921 | 929 | ||
930 | err = of_get_dma_window(dev->of_node, NULL, 0, NULL, &base, &size); | ||
931 | if (err) | ||
932 | return -ENODEV; | ||
933 | |||
934 | if (size & SMMU_PAGE_MASK) | ||
935 | return -EINVAL; | ||
936 | |||
937 | size >>= SMMU_PAGE_SHIFT; | ||
938 | if (!size) | ||
939 | return -EINVAL; | ||
940 | |||
941 | prop = of_get_property(dev->of_node, "nvidia,#asids", NULL); | ||
942 | if (!prop) | ||
943 | return -ENODEV; | ||
944 | asids = be32_to_cpup(prop); | ||
945 | if (!asids) | ||
946 | return -ENODEV; | ||
947 | |||
948 | smmu->ahb = of_parse_phandle(dev->of_node, "nvidia,ahb", 0); | ||
949 | if (!smmu->ahb) | ||
950 | return -ENODEV; | ||
951 | |||
952 | smmu->dev = dev; | ||
953 | smmu->num_as = asids; | ||
954 | smmu->iovmm_base = base; | ||
955 | smmu->page_count = size; | ||
956 | |||
922 | smmu->translation_enable_0 = ~0; | 957 | smmu->translation_enable_0 = ~0; |
923 | smmu->translation_enable_1 = ~0; | 958 | smmu->translation_enable_1 = ~0; |
924 | smmu->translation_enable_2 = ~0; | 959 | smmu->translation_enable_2 = ~0; |
@@ -945,7 +980,9 @@ static int tegra_smmu_probe(struct platform_device *pdev) | |||
945 | INIT_LIST_HEAD(&as->client); | 980 | INIT_LIST_HEAD(&as->client); |
946 | } | 981 | } |
947 | spin_lock_init(&smmu->lock); | 982 | spin_lock_init(&smmu->lock); |
948 | smmu_setup_regs(smmu); | 983 | err = smmu_setup_regs(smmu); |
984 | if (err) | ||
985 | goto fail; | ||
949 | platform_set_drvdata(pdev, smmu); | 986 | platform_set_drvdata(pdev, smmu); |
950 | 987 | ||
951 | smmu->avp_vector_page = alloc_page(GFP_KERNEL); | 988 | smmu->avp_vector_page = alloc_page(GFP_KERNEL); |
@@ -958,10 +995,6 @@ static int tegra_smmu_probe(struct platform_device *pdev) | |||
958 | fail: | 995 | fail: |
959 | if (smmu->avp_vector_page) | 996 | if (smmu->avp_vector_page) |
960 | __free_page(smmu->avp_vector_page); | 997 | __free_page(smmu->avp_vector_page); |
961 | if (smmu->regs) | ||
962 | devm_iounmap(dev, smmu->regs); | ||
963 | if (smmu->regs_ahbarb) | ||
964 | devm_iounmap(dev, smmu->regs_ahbarb); | ||
965 | if (smmu && smmu->as) { | 998 | if (smmu && smmu->as) { |
966 | for (i = 0; i < smmu->num_as; i++) { | 999 | for (i = 0; i < smmu->num_as; i++) { |
967 | if (smmu->as[i].pdir_page) { | 1000 | if (smmu->as[i].pdir_page) { |
@@ -993,8 +1026,6 @@ static int tegra_smmu_remove(struct platform_device *pdev) | |||
993 | __free_page(smmu->avp_vector_page); | 1026 | __free_page(smmu->avp_vector_page); |
994 | if (smmu->regs) | 1027 | if (smmu->regs) |
995 | devm_iounmap(dev, smmu->regs); | 1028 | devm_iounmap(dev, smmu->regs); |
996 | if (smmu->regs_ahbarb) | ||
997 | devm_iounmap(dev, smmu->regs_ahbarb); | ||
998 | devm_kfree(dev, smmu); | 1029 | devm_kfree(dev, smmu); |
999 | smmu_handle = NULL; | 1030 | smmu_handle = NULL; |
1000 | return 0; | 1031 | return 0; |
@@ -1005,6 +1036,14 @@ const struct dev_pm_ops tegra_smmu_pm_ops = { | |||
1005 | .resume = tegra_smmu_resume, | 1036 | .resume = tegra_smmu_resume, |
1006 | }; | 1037 | }; |
1007 | 1038 | ||
1039 | #ifdef CONFIG_OF | ||
1040 | static struct of_device_id tegra_smmu_of_match[] __devinitdata = { | ||
1041 | { .compatible = "nvidia,tegra30-smmu", }, | ||
1042 | { }, | ||
1043 | }; | ||
1044 | MODULE_DEVICE_TABLE(of, tegra_smmu_of_match); | ||
1045 | #endif | ||
1046 | |||
1008 | static struct platform_driver tegra_smmu_driver = { | 1047 | static struct platform_driver tegra_smmu_driver = { |
1009 | .probe = tegra_smmu_probe, | 1048 | .probe = tegra_smmu_probe, |
1010 | .remove = tegra_smmu_remove, | 1049 | .remove = tegra_smmu_remove, |
@@ -1012,6 +1051,7 @@ static struct platform_driver tegra_smmu_driver = { | |||
1012 | .owner = THIS_MODULE, | 1051 | .owner = THIS_MODULE, |
1013 | .name = "tegra-smmu", | 1052 | .name = "tegra-smmu", |
1014 | .pm = &tegra_smmu_pm_ops, | 1053 | .pm = &tegra_smmu_pm_ops, |
1054 | .of_match_table = of_match_ptr(tegra_smmu_of_match), | ||
1015 | }, | 1055 | }, |
1016 | }; | 1056 | }; |
1017 | 1057 | ||
@@ -1031,4 +1071,5 @@ module_exit(tegra_smmu_exit); | |||
1031 | 1071 | ||
1032 | MODULE_DESCRIPTION("IOMMU API for SMMU in Tegra30"); | 1072 | MODULE_DESCRIPTION("IOMMU API for SMMU in Tegra30"); |
1033 | MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); | 1073 | MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); |
1074 | MODULE_ALIAS("platform:tegra-smmu"); | ||
1034 | MODULE_LICENSE("GPL v2"); | 1075 | MODULE_LICENSE("GPL v2"); |