diff options
-rw-r--r-- | Documentation/devicetree/bindings/iommu/nvidia,tegra30-smmu.txt | 21 | ||||
-rw-r--r-- | drivers/iommu/Kconfig | 6 | ||||
-rw-r--r-- | drivers/iommu/Makefile | 1 | ||||
-rw-r--r-- | drivers/iommu/of_iommu.c | 90 | ||||
-rw-r--r-- | drivers/iommu/tegra-smmu.c | 280 | ||||
-rw-r--r-- | include/linux/of_iommu.h | 21 |
6 files changed, 287 insertions, 132 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 340893727538..9f69b561f5db 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig | |||
@@ -13,6 +13,10 @@ menuconfig IOMMU_SUPPORT | |||
13 | 13 | ||
14 | if IOMMU_SUPPORT | 14 | if IOMMU_SUPPORT |
15 | 15 | ||
16 | config OF_IOMMU | ||
17 | def_bool y | ||
18 | depends on OF | ||
19 | |||
16 | # MSM IOMMU support | 20 | # MSM IOMMU support |
17 | config MSM_IOMMU | 21 | config MSM_IOMMU |
18 | bool "MSM IOMMU Support" | 22 | bool "MSM IOMMU Support" |
@@ -154,7 +158,7 @@ config TEGRA_IOMMU_GART | |||
154 | 158 | ||
155 | config TEGRA_IOMMU_SMMU | 159 | config TEGRA_IOMMU_SMMU |
156 | bool "Tegra SMMU IOMMU Support" | 160 | bool "Tegra SMMU IOMMU Support" |
157 | depends on ARCH_TEGRA_3x_SOC | 161 | depends on ARCH_TEGRA_3x_SOC && TEGRA_AHB |
158 | select IOMMU_API | 162 | select IOMMU_API |
159 | help | 163 | help |
160 | Enables support for remapping discontiguous physical memory | 164 | Enables support for remapping discontiguous physical memory |
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 76e54ef796de..14a4d5fc94fa 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | obj-$(CONFIG_IOMMU_API) += iommu.o | 1 | obj-$(CONFIG_IOMMU_API) += iommu.o |
2 | obj-$(CONFIG_OF_IOMMU) += of_iommu.o | ||
2 | obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o | 3 | obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o |
3 | obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o | 4 | obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o |
4 | obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o | 5 | obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o |
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c new file mode 100644 index 000000000000..ee249bc959f8 --- /dev/null +++ b/drivers/iommu/of_iommu.c | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * OF helpers for IOMMU | ||
3 | * | ||
4 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along with | ||
16 | * this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | */ | ||
19 | |||
20 | #include <linux/export.h> | ||
21 | #include <linux/limits.h> | ||
22 | #include <linux/of.h> | ||
23 | |||
24 | /** | ||
25 | * of_get_dma_window - Parse *dma-window property and returns 0 if found. | ||
26 | * | ||
27 | * @dn: device node | ||
28 | * @prefix: prefix for property name if any | ||
29 | * @index: index to start to parse | ||
30 | * @busno: Returns busno if supported. Otherwise pass NULL | ||
31 | * @addr: Returns address that DMA starts | ||
32 | * @size: Returns the range that DMA can handle | ||
33 | * | ||
34 | * This supports different formats flexibly. "prefix" can be | ||
35 | * configured if any. "busno" and "index" are optionally | ||
36 | * specified. Set 0(or NULL) if not used. | ||
37 | */ | ||
38 | int of_get_dma_window(struct device_node *dn, const char *prefix, int index, | ||
39 | unsigned long *busno, dma_addr_t *addr, size_t *size) | ||
40 | { | ||
41 | const __be32 *dma_window, *end; | ||
42 | int bytes, cur_index = 0; | ||
43 | char propname[NAME_MAX], addrname[NAME_MAX], sizename[NAME_MAX]; | ||
44 | |||
45 | if (!dn || !addr || !size) | ||
46 | return -EINVAL; | ||
47 | |||
48 | if (!prefix) | ||
49 | prefix = ""; | ||
50 | |||
51 | snprintf(propname, sizeof(propname), "%sdma-window", prefix); | ||
52 | snprintf(addrname, sizeof(addrname), "%s#dma-address-cells", prefix); | ||
53 | snprintf(sizename, sizeof(sizename), "%s#dma-size-cells", prefix); | ||
54 | |||
55 | dma_window = of_get_property(dn, propname, &bytes); | ||
56 | if (!dma_window) | ||
57 | return -ENODEV; | ||
58 | end = dma_window + bytes / sizeof(*dma_window); | ||
59 | |||
60 | while (dma_window < end) { | ||
61 | u32 cells; | ||
62 | const void *prop; | ||
63 | |||
64 | /* busno is one cell if supported */ | ||
65 | if (busno) | ||
66 | *busno = be32_to_cpup(dma_window++); | ||
67 | |||
68 | prop = of_get_property(dn, addrname, NULL); | ||
69 | if (!prop) | ||
70 | prop = of_get_property(dn, "#address-cells", NULL); | ||
71 | |||
72 | cells = prop ? be32_to_cpup(prop) : of_n_addr_cells(dn); | ||
73 | if (!cells) | ||
74 | return -EINVAL; | ||
75 | *addr = of_read_number(dma_window, cells); | ||
76 | dma_window += cells; | ||
77 | |||
78 | prop = of_get_property(dn, sizename, NULL); | ||
79 | cells = prop ? be32_to_cpup(prop) : of_n_size_cells(dn); | ||
80 | if (!cells) | ||
81 | return -EINVAL; | ||
82 | *size = of_read_number(dma_window, cells); | ||
83 | dma_window += cells; | ||
84 | |||
85 | if (cur_index++ == index) | ||
86 | break; | ||
87 | } | ||
88 | return 0; | ||
89 | } | ||
90 | EXPORT_SYMBOL_GPL(of_get_dma_window); | ||
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 3f3d09d560ea..541d210cb421 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,14 +235,12 @@ 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; |
242 | char *name; | 242 | char *name; |
243 | struct device *dev; | 243 | struct device *dev; |
244 | int num_as; | ||
245 | struct smmu_as *as; /* Run-time allocated array */ | ||
246 | struct page *avp_vector_page; /* dummy page shared by all AS's */ | 244 | struct page *avp_vector_page; /* dummy page shared by all AS's */ |
247 | 245 | ||
248 | /* | 246 | /* |
@@ -252,29 +250,50 @@ struct smmu_device { | |||
252 | unsigned long translation_enable_1; | 250 | unsigned long translation_enable_1; |
253 | unsigned long translation_enable_2; | 251 | unsigned long translation_enable_2; |
254 | unsigned long asid_security; | 252 | unsigned long asid_security; |
253 | |||
254 | struct device_node *ahb; | ||
255 | |||
256 | int num_as; | ||
257 | struct smmu_as as[0]; /* Run-time allocated array */ | ||
255 | }; | 258 | }; |
256 | 259 | ||
257 | static struct smmu_device *smmu_handle; /* unique for a system */ | 260 | static struct smmu_device *smmu_handle; /* unique for a system */ |
258 | 261 | ||
259 | /* | 262 | /* |
260 | * SMMU/AHB register accessors | 263 | * SMMU register accessors |
261 | */ | 264 | */ |
262 | static inline u32 smmu_read(struct smmu_device *smmu, size_t offs) | 265 | static inline u32 smmu_read(struct smmu_device *smmu, size_t offs) |
263 | { | 266 | { |
264 | return readl(smmu->regs + offs); | 267 | BUG_ON(offs < 0x10); |
265 | } | 268 | if (offs < 0x3c) |
266 | static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs) | 269 | return readl(smmu->regs[0] + offs - 0x10); |
267 | { | 270 | BUG_ON(offs < 0x1f0); |
268 | writel(val, smmu->regs + offs); | 271 | if (offs < 0x200) |
272 | return readl(smmu->regs[1] + offs - 0x1f0); | ||
273 | BUG_ON(offs < 0x228); | ||
274 | if (offs < 0x284) | ||
275 | return readl(smmu->regs[2] + offs - 0x228); | ||
276 | BUG(); | ||
269 | } | 277 | } |
270 | 278 | ||
271 | static inline u32 ahb_read(struct smmu_device *smmu, size_t offs) | 279 | 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 | { | 280 | { |
277 | writel(val, smmu->regs_ahbarb + offs); | 281 | BUG_ON(offs < 0x10); |
282 | if (offs < 0x3c) { | ||
283 | writel(val, smmu->regs[0] + offs - 0x10); | ||
284 | return; | ||
285 | } | ||
286 | BUG_ON(offs < 0x1f0); | ||
287 | if (offs < 0x200) { | ||
288 | writel(val, smmu->regs[1] + offs - 0x1f0); | ||
289 | return; | ||
290 | } | ||
291 | BUG_ON(offs < 0x228); | ||
292 | if (offs < 0x284) { | ||
293 | writel(val, smmu->regs[2] + offs - 0x228); | ||
294 | return; | ||
295 | } | ||
296 | BUG(); | ||
278 | } | 297 | } |
279 | 298 | ||
280 | #define VA_PAGE_TO_PA(va, page) \ | 299 | #define VA_PAGE_TO_PA(va, page) \ |
@@ -370,7 +389,7 @@ static void smmu_flush_regs(struct smmu_device *smmu, int enable) | |||
370 | FLUSH_SMMU_REGS(smmu); | 389 | FLUSH_SMMU_REGS(smmu); |
371 | } | 390 | } |
372 | 391 | ||
373 | static void smmu_setup_regs(struct smmu_device *smmu) | 392 | static int smmu_setup_regs(struct smmu_device *smmu) |
374 | { | 393 | { |
375 | int i; | 394 | int i; |
376 | u32 val; | 395 | u32 val; |
@@ -398,10 +417,7 @@ static void smmu_setup_regs(struct smmu_device *smmu) | |||
398 | 417 | ||
399 | smmu_flush_regs(smmu, 1); | 418 | smmu_flush_regs(smmu, 1); |
400 | 419 | ||
401 | val = ahb_read(smmu, AHB_XBAR_CTRL); | 420 | 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 | } | 421 | } |
406 | 422 | ||
407 | static void flush_ptc_and_tlb(struct smmu_device *smmu, | 423 | static void flush_ptc_and_tlb(struct smmu_device *smmu, |
@@ -537,33 +553,42 @@ static inline void put_signature(struct smmu_as *as, | |||
537 | #endif | 553 | #endif |
538 | 554 | ||
539 | /* | 555 | /* |
540 | * Caller must lock/unlock as | 556 | * Caller must not hold as->lock |
541 | */ | 557 | */ |
542 | static int alloc_pdir(struct smmu_as *as) | 558 | static int alloc_pdir(struct smmu_as *as) |
543 | { | 559 | { |
544 | unsigned long *pdir; | 560 | unsigned long *pdir, flags; |
545 | int pdn; | 561 | int pdn, err = 0; |
546 | u32 val; | 562 | u32 val; |
547 | struct smmu_device *smmu = as->smmu; | 563 | struct smmu_device *smmu = as->smmu; |
564 | struct page *page; | ||
565 | unsigned int *cnt; | ||
548 | 566 | ||
549 | if (as->pdir_page) | 567 | /* |
550 | return 0; | 568 | * do the allocation, then grab as->lock |
569 | */ | ||
570 | cnt = devm_kzalloc(smmu->dev, | ||
571 | sizeof(cnt[0]) * SMMU_PDIR_COUNT, | ||
572 | GFP_KERNEL); | ||
573 | page = alloc_page(GFP_KERNEL | __GFP_DMA); | ||
551 | 574 | ||
552 | as->pte_count = devm_kzalloc(smmu->dev, | 575 | spin_lock_irqsave(&as->lock, flags); |
553 | sizeof(as->pte_count[0]) * SMMU_PDIR_COUNT, GFP_ATOMIC); | 576 | |
554 | if (!as->pte_count) { | 577 | if (as->pdir_page) { |
555 | dev_err(smmu->dev, | 578 | /* We raced, free the redundant */ |
556 | "failed to allocate smmu_device PTE cunters\n"); | 579 | err = -EAGAIN; |
557 | return -ENOMEM; | 580 | goto err_out; |
558 | } | 581 | } |
559 | as->pdir_page = alloc_page(GFP_ATOMIC | __GFP_DMA); | 582 | |
560 | if (!as->pdir_page) { | 583 | if (!page || !cnt) { |
561 | dev_err(smmu->dev, | 584 | dev_err(smmu->dev, "failed to allocate at %s\n", __func__); |
562 | "failed to allocate smmu_device page directory\n"); | 585 | err = -ENOMEM; |
563 | devm_kfree(smmu->dev, as->pte_count); | 586 | goto err_out; |
564 | as->pte_count = NULL; | ||
565 | return -ENOMEM; | ||
566 | } | 587 | } |
588 | |||
589 | as->pdir_page = page; | ||
590 | as->pte_count = cnt; | ||
591 | |||
567 | SetPageReserved(as->pdir_page); | 592 | SetPageReserved(as->pdir_page); |
568 | pdir = page_address(as->pdir_page); | 593 | pdir = page_address(as->pdir_page); |
569 | 594 | ||
@@ -579,7 +604,17 @@ static int alloc_pdir(struct smmu_as *as) | |||
579 | smmu_write(smmu, val, SMMU_TLB_FLUSH); | 604 | smmu_write(smmu, val, SMMU_TLB_FLUSH); |
580 | FLUSH_SMMU_REGS(as->smmu); | 605 | FLUSH_SMMU_REGS(as->smmu); |
581 | 606 | ||
607 | spin_unlock_irqrestore(&as->lock, flags); | ||
608 | |||
582 | return 0; | 609 | return 0; |
610 | |||
611 | err_out: | ||
612 | spin_unlock_irqrestore(&as->lock, flags); | ||
613 | |||
614 | devm_kfree(smmu->dev, cnt); | ||
615 | if (page) | ||
616 | __free_page(page); | ||
617 | return err; | ||
583 | } | 618 | } |
584 | 619 | ||
585 | static void __smmu_iommu_unmap(struct smmu_as *as, dma_addr_t iova) | 620 | static void __smmu_iommu_unmap(struct smmu_as *as, dma_addr_t iova) |
@@ -771,30 +806,28 @@ out: | |||
771 | 806 | ||
772 | static int smmu_iommu_domain_init(struct iommu_domain *domain) | 807 | static int smmu_iommu_domain_init(struct iommu_domain *domain) |
773 | { | 808 | { |
774 | int i; | 809 | int i, err = -ENODEV; |
775 | unsigned long flags; | 810 | unsigned long flags; |
776 | struct smmu_as *as; | 811 | struct smmu_as *as; |
777 | struct smmu_device *smmu = smmu_handle; | 812 | struct smmu_device *smmu = smmu_handle; |
778 | 813 | ||
779 | /* Look for a free AS with lock held */ | 814 | /* Look for a free AS with lock held */ |
780 | for (i = 0; i < smmu->num_as; i++) { | 815 | for (i = 0; i < smmu->num_as; i++) { |
781 | struct smmu_as *tmp = &smmu->as[i]; | 816 | as = &smmu->as[i]; |
782 | 817 | if (!as->pdir_page) { | |
783 | spin_lock_irqsave(&tmp->lock, flags); | 818 | err = alloc_pdir(as); |
784 | if (!tmp->pdir_page) { | 819 | if (!err) |
785 | as = tmp; | 820 | goto found; |
786 | goto found; | ||
787 | } | 821 | } |
788 | spin_unlock_irqrestore(&tmp->lock, flags); | 822 | if (err != -EAGAIN) |
823 | break; | ||
789 | } | 824 | } |
790 | dev_err(smmu->dev, "no free AS\n"); | 825 | if (i == smmu->num_as) |
791 | return -ENODEV; | 826 | dev_err(smmu->dev, "no free AS\n"); |
827 | return err; | ||
792 | 828 | ||
793 | found: | 829 | found: |
794 | if (alloc_pdir(as) < 0) | 830 | spin_lock_irqsave(&smmu->lock, flags); |
795 | goto err_alloc_pdir; | ||
796 | |||
797 | spin_lock(&smmu->lock); | ||
798 | 831 | ||
799 | /* Update PDIR register */ | 832 | /* Update PDIR register */ |
800 | smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID); | 833 | smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID); |
@@ -802,17 +835,13 @@ found: | |||
802 | SMMU_MK_PDIR(as->pdir_page, as->pdir_attr), SMMU_PTB_DATA); | 835 | SMMU_MK_PDIR(as->pdir_page, as->pdir_attr), SMMU_PTB_DATA); |
803 | FLUSH_SMMU_REGS(smmu); | 836 | FLUSH_SMMU_REGS(smmu); |
804 | 837 | ||
805 | spin_unlock(&smmu->lock); | 838 | spin_unlock_irqrestore(&smmu->lock, flags); |
806 | 839 | ||
807 | spin_unlock_irqrestore(&as->lock, flags); | ||
808 | domain->priv = as; | 840 | domain->priv = as; |
809 | 841 | ||
810 | dev_dbg(smmu->dev, "smmu_as@%p\n", as); | 842 | dev_dbg(smmu->dev, "smmu_as@%p\n", as); |
811 | return 0; | ||
812 | 843 | ||
813 | err_alloc_pdir: | 844 | return 0; |
814 | spin_unlock_irqrestore(&as->lock, flags); | ||
815 | return -ENODEV; | ||
816 | } | 845 | } |
817 | 846 | ||
818 | static void smmu_iommu_domain_destroy(struct iommu_domain *domain) | 847 | static void smmu_iommu_domain_destroy(struct iommu_domain *domain) |
@@ -873,65 +902,73 @@ static int tegra_smmu_resume(struct device *dev) | |||
873 | { | 902 | { |
874 | struct smmu_device *smmu = dev_get_drvdata(dev); | 903 | struct smmu_device *smmu = dev_get_drvdata(dev); |
875 | unsigned long flags; | 904 | unsigned long flags; |
905 | int err; | ||
876 | 906 | ||
877 | spin_lock_irqsave(&smmu->lock, flags); | 907 | spin_lock_irqsave(&smmu->lock, flags); |
878 | smmu_setup_regs(smmu); | 908 | err = smmu_setup_regs(smmu); |
879 | spin_unlock_irqrestore(&smmu->lock, flags); | 909 | spin_unlock_irqrestore(&smmu->lock, flags); |
880 | return 0; | 910 | return err; |
881 | } | 911 | } |
882 | 912 | ||
883 | static int tegra_smmu_probe(struct platform_device *pdev) | 913 | static int tegra_smmu_probe(struct platform_device *pdev) |
884 | { | 914 | { |
885 | struct smmu_device *smmu; | 915 | struct smmu_device *smmu; |
886 | struct resource *regs, *regs2, *window; | ||
887 | struct device *dev = &pdev->dev; | 916 | struct device *dev = &pdev->dev; |
888 | int i, err = 0; | 917 | int i, asids, err = 0; |
918 | dma_addr_t uninitialized_var(base); | ||
919 | size_t bytes, uninitialized_var(size); | ||
889 | 920 | ||
890 | if (smmu_handle) | 921 | if (smmu_handle) |
891 | return -EIO; | 922 | return -EIO; |
892 | 923 | ||
893 | BUILD_BUG_ON(PAGE_SHIFT != SMMU_PAGE_SHIFT); | 924 | BUILD_BUG_ON(PAGE_SHIFT != SMMU_PAGE_SHIFT); |
894 | 925 | ||
895 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 926 | if (of_property_read_u32(dev->of_node, "nvidia,#asids", &asids)) |
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; | 927 | return -ENODEV; |
901 | } | ||
902 | 928 | ||
903 | smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); | 929 | bytes = sizeof(*smmu) + asids * sizeof(*smmu->as); |
930 | smmu = devm_kzalloc(dev, bytes, GFP_KERNEL); | ||
904 | if (!smmu) { | 931 | if (!smmu) { |
905 | dev_err(dev, "failed to allocate smmu_device\n"); | 932 | dev_err(dev, "failed to allocate smmu_device\n"); |
906 | return -ENOMEM; | 933 | return -ENOMEM; |
907 | } | 934 | } |
908 | 935 | ||
909 | smmu->dev = dev; | 936 | for (i = 0; i < ARRAY_SIZE(smmu->regs); i++) { |
910 | smmu->num_as = SMMU_NUM_ASIDS; | 937 | struct resource *res; |
911 | smmu->iovmm_base = (unsigned long)window->start; | 938 | |
912 | smmu->page_count = resource_size(window) >> SMMU_PAGE_SHIFT; | 939 | res = platform_get_resource(pdev, IORESOURCE_MEM, i); |
913 | smmu->regs = devm_ioremap(dev, regs->start, resource_size(regs)); | 940 | if (!res) |
914 | smmu->regs_ahbarb = devm_ioremap(dev, regs2->start, | 941 | return -ENODEV; |
915 | resource_size(regs2)); | 942 | smmu->regs[i] = devm_request_and_ioremap(&pdev->dev, res); |
916 | if (!smmu->regs || !smmu->regs_ahbarb) { | 943 | if (!smmu->regs[i]) |
917 | dev_err(dev, "failed to remap SMMU registers\n"); | 944 | return -EBUSY; |
918 | err = -ENXIO; | ||
919 | goto fail; | ||
920 | } | 945 | } |
921 | 946 | ||
947 | err = of_get_dma_window(dev->of_node, NULL, 0, NULL, &base, &size); | ||
948 | if (err) | ||
949 | return -ENODEV; | ||
950 | |||
951 | if (size & SMMU_PAGE_MASK) | ||
952 | return -EINVAL; | ||
953 | |||
954 | size >>= SMMU_PAGE_SHIFT; | ||
955 | if (!size) | ||
956 | return -EINVAL; | ||
957 | |||
958 | smmu->ahb = of_parse_phandle(dev->of_node, "nvidia,ahb", 0); | ||
959 | if (!smmu->ahb) | ||
960 | return -ENODEV; | ||
961 | |||
962 | smmu->dev = dev; | ||
963 | smmu->num_as = asids; | ||
964 | smmu->iovmm_base = base; | ||
965 | smmu->page_count = size; | ||
966 | |||
922 | smmu->translation_enable_0 = ~0; | 967 | smmu->translation_enable_0 = ~0; |
923 | smmu->translation_enable_1 = ~0; | 968 | smmu->translation_enable_1 = ~0; |
924 | smmu->translation_enable_2 = ~0; | 969 | smmu->translation_enable_2 = ~0; |
925 | smmu->asid_security = 0; | 970 | smmu->asid_security = 0; |
926 | 971 | ||
927 | smmu->as = devm_kzalloc(dev, | ||
928 | sizeof(smmu->as[0]) * smmu->num_as, GFP_KERNEL); | ||
929 | if (!smmu->as) { | ||
930 | dev_err(dev, "failed to allocate smmu_as\n"); | ||
931 | err = -ENOMEM; | ||
932 | goto fail; | ||
933 | } | ||
934 | |||
935 | for (i = 0; i < smmu->num_as; i++) { | 972 | for (i = 0; i < smmu->num_as; i++) { |
936 | struct smmu_as *as = &smmu->as[i]; | 973 | struct smmu_as *as = &smmu->as[i]; |
937 | 974 | ||
@@ -945,57 +982,28 @@ static int tegra_smmu_probe(struct platform_device *pdev) | |||
945 | INIT_LIST_HEAD(&as->client); | 982 | INIT_LIST_HEAD(&as->client); |
946 | } | 983 | } |
947 | spin_lock_init(&smmu->lock); | 984 | spin_lock_init(&smmu->lock); |
948 | smmu_setup_regs(smmu); | 985 | err = smmu_setup_regs(smmu); |
986 | if (err) | ||
987 | return err; | ||
949 | platform_set_drvdata(pdev, smmu); | 988 | platform_set_drvdata(pdev, smmu); |
950 | 989 | ||
951 | smmu->avp_vector_page = alloc_page(GFP_KERNEL); | 990 | smmu->avp_vector_page = alloc_page(GFP_KERNEL); |
952 | if (!smmu->avp_vector_page) | 991 | if (!smmu->avp_vector_page) |
953 | goto fail; | 992 | return -ENOMEM; |
954 | 993 | ||
955 | smmu_handle = smmu; | 994 | smmu_handle = smmu; |
956 | return 0; | 995 | return 0; |
957 | |||
958 | fail: | ||
959 | if (smmu->avp_vector_page) | ||
960 | __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) { | ||
966 | for (i = 0; i < smmu->num_as; i++) { | ||
967 | if (smmu->as[i].pdir_page) { | ||
968 | ClearPageReserved(smmu->as[i].pdir_page); | ||
969 | __free_page(smmu->as[i].pdir_page); | ||
970 | } | ||
971 | } | ||
972 | devm_kfree(dev, smmu->as); | ||
973 | } | ||
974 | devm_kfree(dev, smmu); | ||
975 | return err; | ||
976 | } | 996 | } |
977 | 997 | ||
978 | static int tegra_smmu_remove(struct platform_device *pdev) | 998 | static int tegra_smmu_remove(struct platform_device *pdev) |
979 | { | 999 | { |
980 | struct smmu_device *smmu = platform_get_drvdata(pdev); | 1000 | struct smmu_device *smmu = platform_get_drvdata(pdev); |
981 | struct device *dev = smmu->dev; | 1001 | int i; |
982 | 1002 | ||
983 | smmu_write(smmu, SMMU_CONFIG_DISABLE, SMMU_CONFIG); | 1003 | smmu_write(smmu, SMMU_CONFIG_DISABLE, SMMU_CONFIG); |
984 | platform_set_drvdata(pdev, NULL); | 1004 | for (i = 0; i < smmu->num_as; i++) |
985 | if (smmu->as) { | 1005 | free_pdir(&smmu->as[i]); |
986 | int i; | 1006 | __free_page(smmu->avp_vector_page); |
987 | |||
988 | for (i = 0; i < smmu->num_as; i++) | ||
989 | free_pdir(&smmu->as[i]); | ||
990 | devm_kfree(dev, smmu->as); | ||
991 | } | ||
992 | if (smmu->avp_vector_page) | ||
993 | __free_page(smmu->avp_vector_page); | ||
994 | if (smmu->regs) | ||
995 | devm_iounmap(dev, smmu->regs); | ||
996 | if (smmu->regs_ahbarb) | ||
997 | devm_iounmap(dev, smmu->regs_ahbarb); | ||
998 | devm_kfree(dev, smmu); | ||
999 | smmu_handle = NULL; | 1007 | smmu_handle = NULL; |
1000 | return 0; | 1008 | return 0; |
1001 | } | 1009 | } |
@@ -1005,6 +1013,14 @@ const struct dev_pm_ops tegra_smmu_pm_ops = { | |||
1005 | .resume = tegra_smmu_resume, | 1013 | .resume = tegra_smmu_resume, |
1006 | }; | 1014 | }; |
1007 | 1015 | ||
1016 | #ifdef CONFIG_OF | ||
1017 | static struct of_device_id tegra_smmu_of_match[] __devinitdata = { | ||
1018 | { .compatible = "nvidia,tegra30-smmu", }, | ||
1019 | { }, | ||
1020 | }; | ||
1021 | MODULE_DEVICE_TABLE(of, tegra_smmu_of_match); | ||
1022 | #endif | ||
1023 | |||
1008 | static struct platform_driver tegra_smmu_driver = { | 1024 | static struct platform_driver tegra_smmu_driver = { |
1009 | .probe = tegra_smmu_probe, | 1025 | .probe = tegra_smmu_probe, |
1010 | .remove = tegra_smmu_remove, | 1026 | .remove = tegra_smmu_remove, |
@@ -1012,6 +1028,7 @@ static struct platform_driver tegra_smmu_driver = { | |||
1012 | .owner = THIS_MODULE, | 1028 | .owner = THIS_MODULE, |
1013 | .name = "tegra-smmu", | 1029 | .name = "tegra-smmu", |
1014 | .pm = &tegra_smmu_pm_ops, | 1030 | .pm = &tegra_smmu_pm_ops, |
1031 | .of_match_table = of_match_ptr(tegra_smmu_of_match), | ||
1015 | }, | 1032 | }, |
1016 | }; | 1033 | }; |
1017 | 1034 | ||
@@ -1031,4 +1048,5 @@ module_exit(tegra_smmu_exit); | |||
1031 | 1048 | ||
1032 | MODULE_DESCRIPTION("IOMMU API for SMMU in Tegra30"); | 1049 | MODULE_DESCRIPTION("IOMMU API for SMMU in Tegra30"); |
1033 | MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); | 1050 | MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>"); |
1051 | MODULE_ALIAS("platform:tegra-smmu"); | ||
1034 | MODULE_LICENSE("GPL v2"); | 1052 | MODULE_LICENSE("GPL v2"); |
diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h new file mode 100644 index 000000000000..51a560f34bca --- /dev/null +++ b/include/linux/of_iommu.h | |||
@@ -0,0 +1,21 @@ | |||
1 | #ifndef __OF_IOMMU_H | ||
2 | #define __OF_IOMMU_H | ||
3 | |||
4 | #ifdef CONFIG_OF_IOMMU | ||
5 | |||
6 | extern int of_get_dma_window(struct device_node *dn, const char *prefix, | ||
7 | int index, unsigned long *busno, dma_addr_t *addr, | ||
8 | size_t *size); | ||
9 | |||
10 | #else | ||
11 | |||
12 | static inline int of_get_dma_window(struct device_node *dn, const char *prefix, | ||
13 | int index, unsigned long *busno, dma_addr_t *addr, | ||
14 | size_t *size) | ||
15 | { | ||
16 | return -EINVAL; | ||
17 | } | ||
18 | |||
19 | #endif /* CONFIG_OF_IOMMU */ | ||
20 | |||
21 | #endif /* __OF_IOMMU_H */ | ||