diff options
author | Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | 2014-04-02 06:47:37 -0400 |
---|---|---|
committer | Joerg Roedel <jroedel@suse.de> | 2014-05-26 05:18:21 -0400 |
commit | d25a2a16f0889de4a1cd8639896f35dc9465f6f5 (patch) | |
tree | 3b7e1425317bbc6bd965a05c47e87aff4f2be26d | |
parent | 4b660a7f5c8099d88d1a43d8ae138965112592c7 (diff) |
iommu: Add driver for Renesas VMSA-compatible IPMMU
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
-rw-r--r-- | drivers/iommu/Kconfig | 12 | ||||
-rw-r--r-- | drivers/iommu/Makefile | 1 | ||||
-rw-r--r-- | drivers/iommu/ipmmu-vmsa.c | 1070 | ||||
-rw-r--r-- | include/linux/platform_data/ipmmu-vmsa.h | 24 |
4 files changed, 1107 insertions, 0 deletions
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index df56e4c74a7e..a22b537caacd 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig | |||
@@ -272,6 +272,18 @@ config SHMOBILE_IOMMU_L1SIZE | |||
272 | default 256 if SHMOBILE_IOMMU_ADDRSIZE_64MB | 272 | default 256 if SHMOBILE_IOMMU_ADDRSIZE_64MB |
273 | default 128 if SHMOBILE_IOMMU_ADDRSIZE_32MB | 273 | default 128 if SHMOBILE_IOMMU_ADDRSIZE_32MB |
274 | 274 | ||
275 | config IPMMU_VMSA | ||
276 | bool "Renesas VMSA-compatible IPMMU" | ||
277 | depends on ARM_LPAE | ||
278 | depends on ARCH_SHMOBILE || COMPILE_TEST | ||
279 | select IOMMU_API | ||
280 | select ARM_DMA_USE_IOMMU | ||
281 | help | ||
282 | Support for the Renesas VMSA-compatible IPMMU Renesas found in the | ||
283 | R-Mobile APE6 and R-Car H2/M2 SoCs. | ||
284 | |||
285 | If unsure, say N. | ||
286 | |||
275 | config SPAPR_TCE_IOMMU | 287 | config SPAPR_TCE_IOMMU |
276 | bool "sPAPR TCE IOMMU Support" | 288 | bool "sPAPR TCE IOMMU Support" |
277 | depends on PPC_POWERNV || PPC_PSERIES | 289 | depends on PPC_POWERNV || PPC_PSERIES |
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 5d58bf16e9e3..8893bad048e0 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile | |||
@@ -7,6 +7,7 @@ obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o | |||
7 | obj-$(CONFIG_ARM_SMMU) += arm-smmu.o | 7 | obj-$(CONFIG_ARM_SMMU) += arm-smmu.o |
8 | obj-$(CONFIG_DMAR_TABLE) += dmar.o | 8 | obj-$(CONFIG_DMAR_TABLE) += dmar.o |
9 | obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o | 9 | obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o |
10 | obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o | ||
10 | obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o | 11 | obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o |
11 | obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o | 12 | obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o |
12 | obj-$(CONFIG_OMAP_IOMMU) += omap-iommu2.o | 13 | obj-$(CONFIG_OMAP_IOMMU) += omap-iommu2.o |
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c new file mode 100644 index 000000000000..b084530babf4 --- /dev/null +++ b/drivers/iommu/ipmmu-vmsa.c | |||
@@ -0,0 +1,1070 @@ | |||
1 | /* | ||
2 | * IPMMU VMSA | ||
3 | * | ||
4 | * Copyright (C) 2014 Renesas Electronics Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 of the License. | ||
9 | */ | ||
10 | |||
11 | #include <linux/delay.h> | ||
12 | #include <linux/dma-mapping.h> | ||
13 | #include <linux/err.h> | ||
14 | #include <linux/export.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/iommu.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/platform_data/ipmmu-vmsa.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/sizes.h> | ||
22 | #include <linux/slab.h> | ||
23 | |||
24 | #include <asm/dma-iommu.h> | ||
25 | #include <asm/pgalloc.h> | ||
26 | |||
27 | struct ipmmu_vmsa_device { | ||
28 | struct device *dev; | ||
29 | void __iomem *base; | ||
30 | struct list_head list; | ||
31 | |||
32 | const struct ipmmu_vmsa_platform_data *pdata; | ||
33 | unsigned int num_utlbs; | ||
34 | |||
35 | struct dma_iommu_mapping *mapping; | ||
36 | }; | ||
37 | |||
38 | struct ipmmu_vmsa_domain { | ||
39 | struct ipmmu_vmsa_device *mmu; | ||
40 | struct iommu_domain *io_domain; | ||
41 | |||
42 | unsigned int context_id; | ||
43 | spinlock_t lock; /* Protects mappings */ | ||
44 | pgd_t *pgd; | ||
45 | }; | ||
46 | |||
47 | static DEFINE_SPINLOCK(ipmmu_devices_lock); | ||
48 | static LIST_HEAD(ipmmu_devices); | ||
49 | |||
50 | #define TLB_LOOP_TIMEOUT 100 /* 100us */ | ||
51 | |||
52 | /* ----------------------------------------------------------------------------- | ||
53 | * Registers Definition | ||
54 | */ | ||
55 | |||
56 | #define IM_CTX_SIZE 0x40 | ||
57 | |||
58 | #define IMCTR 0x0000 | ||
59 | #define IMCTR_TRE (1 << 17) | ||
60 | #define IMCTR_AFE (1 << 16) | ||
61 | #define IMCTR_RTSEL_MASK (3 << 4) | ||
62 | #define IMCTR_RTSEL_SHIFT 4 | ||
63 | #define IMCTR_TREN (1 << 3) | ||
64 | #define IMCTR_INTEN (1 << 2) | ||
65 | #define IMCTR_FLUSH (1 << 1) | ||
66 | #define IMCTR_MMUEN (1 << 0) | ||
67 | |||
68 | #define IMCAAR 0x0004 | ||
69 | |||
70 | #define IMTTBCR 0x0008 | ||
71 | #define IMTTBCR_EAE (1 << 31) | ||
72 | #define IMTTBCR_PMB (1 << 30) | ||
73 | #define IMTTBCR_SH1_NON_SHAREABLE (0 << 28) | ||
74 | #define IMTTBCR_SH1_OUTER_SHAREABLE (2 << 28) | ||
75 | #define IMTTBCR_SH1_INNER_SHAREABLE (3 << 28) | ||
76 | #define IMTTBCR_SH1_MASK (3 << 28) | ||
77 | #define IMTTBCR_ORGN1_NC (0 << 26) | ||
78 | #define IMTTBCR_ORGN1_WB_WA (1 << 26) | ||
79 | #define IMTTBCR_ORGN1_WT (2 << 26) | ||
80 | #define IMTTBCR_ORGN1_WB (3 << 26) | ||
81 | #define IMTTBCR_ORGN1_MASK (3 << 26) | ||
82 | #define IMTTBCR_IRGN1_NC (0 << 24) | ||
83 | #define IMTTBCR_IRGN1_WB_WA (1 << 24) | ||
84 | #define IMTTBCR_IRGN1_WT (2 << 24) | ||
85 | #define IMTTBCR_IRGN1_WB (3 << 24) | ||
86 | #define IMTTBCR_IRGN1_MASK (3 << 24) | ||
87 | #define IMTTBCR_TSZ1_MASK (7 << 16) | ||
88 | #define IMTTBCR_TSZ1_SHIFT 16 | ||
89 | #define IMTTBCR_SH0_NON_SHAREABLE (0 << 12) | ||
90 | #define IMTTBCR_SH0_OUTER_SHAREABLE (2 << 12) | ||
91 | #define IMTTBCR_SH0_INNER_SHAREABLE (3 << 12) | ||
92 | #define IMTTBCR_SH0_MASK (3 << 12) | ||
93 | #define IMTTBCR_ORGN0_NC (0 << 10) | ||
94 | #define IMTTBCR_ORGN0_WB_WA (1 << 10) | ||
95 | #define IMTTBCR_ORGN0_WT (2 << 10) | ||
96 | #define IMTTBCR_ORGN0_WB (3 << 10) | ||
97 | #define IMTTBCR_ORGN0_MASK (3 << 10) | ||
98 | #define IMTTBCR_IRGN0_NC (0 << 8) | ||
99 | #define IMTTBCR_IRGN0_WB_WA (1 << 8) | ||
100 | #define IMTTBCR_IRGN0_WT (2 << 8) | ||
101 | #define IMTTBCR_IRGN0_WB (3 << 8) | ||
102 | #define IMTTBCR_IRGN0_MASK (3 << 8) | ||
103 | #define IMTTBCR_SL0_LVL_2 (0 << 4) | ||
104 | #define IMTTBCR_SL0_LVL_1 (1 << 4) | ||
105 | #define IMTTBCR_TSZ0_MASK (7 << 0) | ||
106 | #define IMTTBCR_TSZ0_SHIFT O | ||
107 | |||
108 | #define IMBUSCR 0x000c | ||
109 | #define IMBUSCR_DVM (1 << 2) | ||
110 | #define IMBUSCR_BUSSEL_SYS (0 << 0) | ||
111 | #define IMBUSCR_BUSSEL_CCI (1 << 0) | ||
112 | #define IMBUSCR_BUSSEL_IMCAAR (2 << 0) | ||
113 | #define IMBUSCR_BUSSEL_CCI_IMCAAR (3 << 0) | ||
114 | #define IMBUSCR_BUSSEL_MASK (3 << 0) | ||
115 | |||
116 | #define IMTTLBR0 0x0010 | ||
117 | #define IMTTUBR0 0x0014 | ||
118 | #define IMTTLBR1 0x0018 | ||
119 | #define IMTTUBR1 0x001c | ||
120 | |||
121 | #define IMSTR 0x0020 | ||
122 | #define IMSTR_ERRLVL_MASK (3 << 12) | ||
123 | #define IMSTR_ERRLVL_SHIFT 12 | ||
124 | #define IMSTR_ERRCODE_TLB_FORMAT (1 << 8) | ||
125 | #define IMSTR_ERRCODE_ACCESS_PERM (4 << 8) | ||
126 | #define IMSTR_ERRCODE_SECURE_ACCESS (5 << 8) | ||
127 | #define IMSTR_ERRCODE_MASK (7 << 8) | ||
128 | #define IMSTR_MHIT (1 << 4) | ||
129 | #define IMSTR_ABORT (1 << 2) | ||
130 | #define IMSTR_PF (1 << 1) | ||
131 | #define IMSTR_TF (1 << 0) | ||
132 | |||
133 | #define IMMAIR0 0x0028 | ||
134 | #define IMMAIR1 0x002c | ||
135 | #define IMMAIR_ATTR_MASK 0xff | ||
136 | #define IMMAIR_ATTR_DEVICE 0x04 | ||
137 | #define IMMAIR_ATTR_NC 0x44 | ||
138 | #define IMMAIR_ATTR_WBRWA 0xff | ||
139 | #define IMMAIR_ATTR_SHIFT(n) ((n) << 3) | ||
140 | #define IMMAIR_ATTR_IDX_NC 0 | ||
141 | #define IMMAIR_ATTR_IDX_WBRWA 1 | ||
142 | #define IMMAIR_ATTR_IDX_DEV 2 | ||
143 | |||
144 | #define IMEAR 0x0030 | ||
145 | |||
146 | #define IMPCTR 0x0200 | ||
147 | #define IMPSTR 0x0208 | ||
148 | #define IMPEAR 0x020c | ||
149 | #define IMPMBA(n) (0x0280 + ((n) * 4)) | ||
150 | #define IMPMBD(n) (0x02c0 + ((n) * 4)) | ||
151 | |||
152 | #define IMUCTR(n) (0x0300 + ((n) * 16)) | ||
153 | #define IMUCTR_FIXADDEN (1 << 31) | ||
154 | #define IMUCTR_FIXADD_MASK (0xff << 16) | ||
155 | #define IMUCTR_FIXADD_SHIFT 16 | ||
156 | #define IMUCTR_TTSEL_MMU(n) ((n) << 4) | ||
157 | #define IMUCTR_TTSEL_PMB (8 << 4) | ||
158 | #define IMUCTR_TTSEL_MASK (15 << 4) | ||
159 | #define IMUCTR_FLUSH (1 << 1) | ||
160 | #define IMUCTR_MMUEN (1 << 0) | ||
161 | |||
162 | #define IMUASID(n) (0x0308 + ((n) * 16)) | ||
163 | #define IMUASID_ASID8_MASK (0xff << 8) | ||
164 | #define IMUASID_ASID8_SHIFT 8 | ||
165 | #define IMUASID_ASID0_MASK (0xff << 0) | ||
166 | #define IMUASID_ASID0_SHIFT 0 | ||
167 | |||
168 | /* ----------------------------------------------------------------------------- | ||
169 | * Page Table Bits | ||
170 | */ | ||
171 | |||
172 | /* | ||
173 | * VMSA states in section B3.6.3 "Control of Secure or Non-secure memory access, | ||
174 | * Long-descriptor format" that the NStable bit being set in a table descriptor | ||
175 | * will result in the NStable and NS bits of all child entries being ignored and | ||
176 | * considered as being set. The IPMMU seems not to comply with this, as it | ||
177 | * generates a secure access page fault if any of the NStable and NS bits isn't | ||
178 | * set when running in non-secure mode. | ||
179 | */ | ||
180 | #ifndef PMD_NSTABLE | ||
181 | #define PMD_NSTABLE (_AT(pmdval_t, 1) << 63) | ||
182 | #endif | ||
183 | |||
184 | #define ARM_VMSA_PTE_XN (((pteval_t)3) << 53) | ||
185 | #define ARM_VMSA_PTE_CONT (((pteval_t)1) << 52) | ||
186 | #define ARM_VMSA_PTE_AF (((pteval_t)1) << 10) | ||
187 | #define ARM_VMSA_PTE_SH_NS (((pteval_t)0) << 8) | ||
188 | #define ARM_VMSA_PTE_SH_OS (((pteval_t)2) << 8) | ||
189 | #define ARM_VMSA_PTE_SH_IS (((pteval_t)3) << 8) | ||
190 | #define ARM_VMSA_PTE_NS (((pteval_t)1) << 5) | ||
191 | #define ARM_VMSA_PTE_PAGE (((pteval_t)3) << 0) | ||
192 | |||
193 | /* Stage-1 PTE */ | ||
194 | #define ARM_VMSA_PTE_AP_UNPRIV (((pteval_t)1) << 6) | ||
195 | #define ARM_VMSA_PTE_AP_RDONLY (((pteval_t)2) << 6) | ||
196 | #define ARM_VMSA_PTE_ATTRINDX_SHIFT 2 | ||
197 | #define ARM_VMSA_PTE_nG (((pteval_t)1) << 11) | ||
198 | |||
199 | /* Stage-2 PTE */ | ||
200 | #define ARM_VMSA_PTE_HAP_FAULT (((pteval_t)0) << 6) | ||
201 | #define ARM_VMSA_PTE_HAP_READ (((pteval_t)1) << 6) | ||
202 | #define ARM_VMSA_PTE_HAP_WRITE (((pteval_t)2) << 6) | ||
203 | #define ARM_VMSA_PTE_MEMATTR_OIWB (((pteval_t)0xf) << 2) | ||
204 | #define ARM_VMSA_PTE_MEMATTR_NC (((pteval_t)0x5) << 2) | ||
205 | #define ARM_VMSA_PTE_MEMATTR_DEV (((pteval_t)0x1) << 2) | ||
206 | |||
207 | /* ----------------------------------------------------------------------------- | ||
208 | * Read/Write Access | ||
209 | */ | ||
210 | |||
211 | static u32 ipmmu_read(struct ipmmu_vmsa_device *mmu, unsigned int offset) | ||
212 | { | ||
213 | return ioread32(mmu->base + offset); | ||
214 | } | ||
215 | |||
216 | static void ipmmu_write(struct ipmmu_vmsa_device *mmu, unsigned int offset, | ||
217 | u32 data) | ||
218 | { | ||
219 | iowrite32(data, mmu->base + offset); | ||
220 | } | ||
221 | |||
222 | static u32 ipmmu_ctx_read(struct ipmmu_vmsa_domain *domain, unsigned int reg) | ||
223 | { | ||
224 | return ipmmu_read(domain->mmu, domain->context_id * IM_CTX_SIZE + reg); | ||
225 | } | ||
226 | |||
227 | static void ipmmu_ctx_write(struct ipmmu_vmsa_domain *domain, unsigned int reg, | ||
228 | u32 data) | ||
229 | { | ||
230 | ipmmu_write(domain->mmu, domain->context_id * IM_CTX_SIZE + reg, data); | ||
231 | } | ||
232 | |||
233 | /* ----------------------------------------------------------------------------- | ||
234 | * TLB and microTLB Management | ||
235 | */ | ||
236 | |||
237 | /* Wait for any pending TLB invalidations to complete */ | ||
238 | static void ipmmu_tlb_sync(struct ipmmu_vmsa_domain *domain) | ||
239 | { | ||
240 | unsigned int count = 0; | ||
241 | |||
242 | while (ipmmu_ctx_read(domain, IMCTR) & IMCTR_FLUSH) { | ||
243 | cpu_relax(); | ||
244 | if (++count == TLB_LOOP_TIMEOUT) { | ||
245 | dev_err_ratelimited(domain->mmu->dev, | ||
246 | "TLB sync timed out -- MMU may be deadlocked\n"); | ||
247 | return; | ||
248 | } | ||
249 | udelay(1); | ||
250 | } | ||
251 | } | ||
252 | |||
253 | static void ipmmu_tlb_invalidate(struct ipmmu_vmsa_domain *domain) | ||
254 | { | ||
255 | u32 reg; | ||
256 | |||
257 | reg = ipmmu_ctx_read(domain, IMCTR); | ||
258 | reg |= IMCTR_FLUSH; | ||
259 | ipmmu_ctx_write(domain, IMCTR, reg); | ||
260 | |||
261 | ipmmu_tlb_sync(domain); | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * Enable MMU translation for the microTLB. | ||
266 | */ | ||
267 | static void ipmmu_utlb_enable(struct ipmmu_vmsa_domain *domain, | ||
268 | const struct ipmmu_vmsa_master *master) | ||
269 | { | ||
270 | struct ipmmu_vmsa_device *mmu = domain->mmu; | ||
271 | |||
272 | /* TODO: What should we set the ASID to ? */ | ||
273 | ipmmu_write(mmu, IMUASID(master->utlb), 0); | ||
274 | /* TODO: Do we need to flush the microTLB ? */ | ||
275 | ipmmu_write(mmu, IMUCTR(master->utlb), | ||
276 | IMUCTR_TTSEL_MMU(domain->context_id) | IMUCTR_FLUSH | | ||
277 | IMUCTR_MMUEN); | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | * Disable MMU translation for the microTLB. | ||
282 | */ | ||
283 | static void ipmmu_utlb_disable(struct ipmmu_vmsa_domain *domain, | ||
284 | const struct ipmmu_vmsa_master *master) | ||
285 | { | ||
286 | struct ipmmu_vmsa_device *mmu = domain->mmu; | ||
287 | |||
288 | ipmmu_write(mmu, IMUCTR(master->utlb), 0); | ||
289 | } | ||
290 | |||
291 | static void ipmmu_flush_pgtable(struct ipmmu_vmsa_device *mmu, void *addr, | ||
292 | size_t size) | ||
293 | { | ||
294 | unsigned long offset = (unsigned long)addr & ~PAGE_MASK; | ||
295 | |||
296 | /* | ||
297 | * TODO: Add support for coherent walk through CCI with DVM and remove | ||
298 | * cache handling. | ||
299 | */ | ||
300 | dma_map_page(mmu->dev, virt_to_page(addr), offset, size, DMA_TO_DEVICE); | ||
301 | } | ||
302 | |||
303 | /* ----------------------------------------------------------------------------- | ||
304 | * Domain/Context Management | ||
305 | */ | ||
306 | |||
307 | static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain) | ||
308 | { | ||
309 | phys_addr_t ttbr; | ||
310 | u32 reg; | ||
311 | |||
312 | /* | ||
313 | * TODO: When adding support for multiple contexts, find an unused | ||
314 | * context. | ||
315 | */ | ||
316 | domain->context_id = 0; | ||
317 | |||
318 | /* TTBR0 */ | ||
319 | ipmmu_flush_pgtable(domain->mmu, domain->pgd, | ||
320 | PTRS_PER_PGD * sizeof(*domain->pgd)); | ||
321 | ttbr = __pa(domain->pgd); | ||
322 | ipmmu_ctx_write(domain, IMTTLBR0, ttbr); | ||
323 | ipmmu_ctx_write(domain, IMTTUBR0, ttbr >> 32); | ||
324 | |||
325 | /* | ||
326 | * TTBCR | ||
327 | * We use long descriptors with inner-shareable WBWA tables and allocate | ||
328 | * the whole 32-bit VA space to TTBR0. | ||
329 | */ | ||
330 | ipmmu_ctx_write(domain, IMTTBCR, IMTTBCR_EAE | | ||
331 | IMTTBCR_SH0_INNER_SHAREABLE | IMTTBCR_ORGN0_WB_WA | | ||
332 | IMTTBCR_IRGN0_WB_WA | IMTTBCR_SL0_LVL_1); | ||
333 | |||
334 | /* | ||
335 | * MAIR0 | ||
336 | * We need three attributes only, non-cacheable, write-back read/write | ||
337 | * allocate and device memory. | ||
338 | */ | ||
339 | reg = (IMMAIR_ATTR_NC << IMMAIR_ATTR_SHIFT(IMMAIR_ATTR_IDX_NC)) | ||
340 | | (IMMAIR_ATTR_WBRWA << IMMAIR_ATTR_SHIFT(IMMAIR_ATTR_IDX_WBRWA)) | ||
341 | | (IMMAIR_ATTR_DEVICE << IMMAIR_ATTR_SHIFT(IMMAIR_ATTR_IDX_DEV)); | ||
342 | ipmmu_ctx_write(domain, IMMAIR0, reg); | ||
343 | |||
344 | /* IMBUSCR */ | ||
345 | ipmmu_ctx_write(domain, IMBUSCR, | ||
346 | ipmmu_ctx_read(domain, IMBUSCR) & | ||
347 | ~(IMBUSCR_DVM | IMBUSCR_BUSSEL_MASK)); | ||
348 | |||
349 | /* | ||
350 | * IMSTR | ||
351 | * Clear all interrupt flags. | ||
352 | */ | ||
353 | ipmmu_ctx_write(domain, IMSTR, ipmmu_ctx_read(domain, IMSTR)); | ||
354 | |||
355 | /* | ||
356 | * IMCTR | ||
357 | * Enable the MMU and interrupt generation. The long-descriptor | ||
358 | * translation table format doesn't use TEX remapping. Don't enable AF | ||
359 | * software management as we have no use for it. Flush the TLB as | ||
360 | * required when modifying the context registers. | ||
361 | */ | ||
362 | ipmmu_ctx_write(domain, IMCTR, IMCTR_INTEN | IMCTR_FLUSH | IMCTR_MMUEN); | ||
363 | |||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | static void ipmmu_domain_destroy_context(struct ipmmu_vmsa_domain *domain) | ||
368 | { | ||
369 | /* | ||
370 | * Disable the context. Flush the TLB as required when modifying the | ||
371 | * context registers. | ||
372 | * | ||
373 | * TODO: Is TLB flush really needed ? | ||
374 | */ | ||
375 | ipmmu_ctx_write(domain, IMCTR, IMCTR_FLUSH); | ||
376 | ipmmu_tlb_sync(domain); | ||
377 | } | ||
378 | |||
379 | /* ----------------------------------------------------------------------------- | ||
380 | * Fault Handling | ||
381 | */ | ||
382 | |||
383 | static irqreturn_t ipmmu_domain_irq(struct ipmmu_vmsa_domain *domain) | ||
384 | { | ||
385 | const u32 err_mask = IMSTR_MHIT | IMSTR_ABORT | IMSTR_PF | IMSTR_TF; | ||
386 | struct ipmmu_vmsa_device *mmu = domain->mmu; | ||
387 | u32 status; | ||
388 | u32 iova; | ||
389 | |||
390 | status = ipmmu_ctx_read(domain, IMSTR); | ||
391 | if (!(status & err_mask)) | ||
392 | return IRQ_NONE; | ||
393 | |||
394 | iova = ipmmu_ctx_read(domain, IMEAR); | ||
395 | |||
396 | /* | ||
397 | * Clear the error status flags. Unlike traditional interrupt flag | ||
398 | * registers that must be cleared by writing 1, this status register | ||
399 | * seems to require 0. The error address register must be read before, | ||
400 | * otherwise its value will be 0. | ||
401 | */ | ||
402 | ipmmu_ctx_write(domain, IMSTR, 0); | ||
403 | |||
404 | /* Log fatal errors. */ | ||
405 | if (status & IMSTR_MHIT) | ||
406 | dev_err_ratelimited(mmu->dev, "Multiple TLB hits @0x%08x\n", | ||
407 | iova); | ||
408 | if (status & IMSTR_ABORT) | ||
409 | dev_err_ratelimited(mmu->dev, "Page Table Walk Abort @0x%08x\n", | ||
410 | iova); | ||
411 | |||
412 | if (!(status & (IMSTR_PF | IMSTR_TF))) | ||
413 | return IRQ_NONE; | ||
414 | |||
415 | /* | ||
416 | * Try to handle page faults and translation faults. | ||
417 | * | ||
418 | * TODO: We need to look up the faulty device based on the I/O VA. Use | ||
419 | * the IOMMU device for now. | ||
420 | */ | ||
421 | if (!report_iommu_fault(domain->io_domain, mmu->dev, iova, 0)) | ||
422 | return IRQ_HANDLED; | ||
423 | |||
424 | dev_err_ratelimited(mmu->dev, | ||
425 | "Unhandled fault: status 0x%08x iova 0x%08x\n", | ||
426 | status, iova); | ||
427 | |||
428 | return IRQ_HANDLED; | ||
429 | } | ||
430 | |||
431 | static irqreturn_t ipmmu_irq(int irq, void *dev) | ||
432 | { | ||
433 | struct ipmmu_vmsa_device *mmu = dev; | ||
434 | struct iommu_domain *io_domain; | ||
435 | struct ipmmu_vmsa_domain *domain; | ||
436 | |||
437 | if (!mmu->mapping) | ||
438 | return IRQ_NONE; | ||
439 | |||
440 | io_domain = mmu->mapping->domain; | ||
441 | domain = io_domain->priv; | ||
442 | |||
443 | return ipmmu_domain_irq(domain); | ||
444 | } | ||
445 | |||
446 | /* ----------------------------------------------------------------------------- | ||
447 | * Page Table Management | ||
448 | */ | ||
449 | |||
450 | static void ipmmu_free_ptes(pmd_t *pmd) | ||
451 | { | ||
452 | pgtable_t table = pmd_pgtable(*pmd); | ||
453 | __free_page(table); | ||
454 | } | ||
455 | |||
456 | static void ipmmu_free_pmds(pud_t *pud) | ||
457 | { | ||
458 | pmd_t *pmd, *pmd_base = pmd_offset(pud, 0); | ||
459 | unsigned int i; | ||
460 | |||
461 | pmd = pmd_base; | ||
462 | for (i = 0; i < PTRS_PER_PMD; ++i) { | ||
463 | if (pmd_none(*pmd)) | ||
464 | continue; | ||
465 | |||
466 | ipmmu_free_ptes(pmd); | ||
467 | pmd++; | ||
468 | } | ||
469 | |||
470 | pmd_free(NULL, pmd_base); | ||
471 | } | ||
472 | |||
473 | static void ipmmu_free_puds(pgd_t *pgd) | ||
474 | { | ||
475 | pud_t *pud, *pud_base = pud_offset(pgd, 0); | ||
476 | unsigned int i; | ||
477 | |||
478 | pud = pud_base; | ||
479 | for (i = 0; i < PTRS_PER_PUD; ++i) { | ||
480 | if (pud_none(*pud)) | ||
481 | continue; | ||
482 | |||
483 | ipmmu_free_pmds(pud); | ||
484 | pud++; | ||
485 | } | ||
486 | |||
487 | pud_free(NULL, pud_base); | ||
488 | } | ||
489 | |||
490 | static void ipmmu_free_pgtables(struct ipmmu_vmsa_domain *domain) | ||
491 | { | ||
492 | pgd_t *pgd, *pgd_base = domain->pgd; | ||
493 | unsigned int i; | ||
494 | |||
495 | /* | ||
496 | * Recursively free the page tables for this domain. We don't care about | ||
497 | * speculative TLB filling, because the TLB will be nuked next time this | ||
498 | * context bank is re-allocated and no devices currently map to these | ||
499 | * tables. | ||
500 | */ | ||
501 | pgd = pgd_base; | ||
502 | for (i = 0; i < PTRS_PER_PGD; ++i) { | ||
503 | if (pgd_none(*pgd)) | ||
504 | continue; | ||
505 | ipmmu_free_puds(pgd); | ||
506 | pgd++; | ||
507 | } | ||
508 | |||
509 | kfree(pgd_base); | ||
510 | } | ||
511 | |||
512 | /* | ||
513 | * We can't use the (pgd|pud|pmd|pte)_populate or the set_(pgd|pud|pmd|pte) | ||
514 | * functions as they would flush the CPU TLB. | ||
515 | */ | ||
516 | |||
517 | static int ipmmu_alloc_init_pte(struct ipmmu_vmsa_device *mmu, pmd_t *pmd, | ||
518 | unsigned long addr, unsigned long end, | ||
519 | phys_addr_t phys, int prot) | ||
520 | { | ||
521 | unsigned long pfn = __phys_to_pfn(phys); | ||
522 | pteval_t pteval = ARM_VMSA_PTE_PAGE | ARM_VMSA_PTE_NS | ARM_VMSA_PTE_AF | ||
523 | | ARM_VMSA_PTE_XN; | ||
524 | pte_t *pte, *start; | ||
525 | |||
526 | if (pmd_none(*pmd)) { | ||
527 | /* Allocate a new set of tables */ | ||
528 | pte = (pte_t *)get_zeroed_page(GFP_ATOMIC); | ||
529 | if (!pte) | ||
530 | return -ENOMEM; | ||
531 | |||
532 | ipmmu_flush_pgtable(mmu, pte, PAGE_SIZE); | ||
533 | *pmd = __pmd(__pa(pte) | PMD_NSTABLE | PMD_TYPE_TABLE); | ||
534 | ipmmu_flush_pgtable(mmu, pmd, sizeof(*pmd)); | ||
535 | |||
536 | pte += pte_index(addr); | ||
537 | } else | ||
538 | pte = pte_offset_kernel(pmd, addr); | ||
539 | |||
540 | pteval |= ARM_VMSA_PTE_AP_UNPRIV | ARM_VMSA_PTE_nG; | ||
541 | if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ)) | ||
542 | pteval |= ARM_VMSA_PTE_AP_RDONLY; | ||
543 | |||
544 | if (prot & IOMMU_CACHE) | ||
545 | pteval |= (IMMAIR_ATTR_IDX_WBRWA << | ||
546 | ARM_VMSA_PTE_ATTRINDX_SHIFT); | ||
547 | |||
548 | /* If no access, create a faulting entry to avoid TLB fills */ | ||
549 | if (prot & IOMMU_EXEC) | ||
550 | pteval &= ~ARM_VMSA_PTE_XN; | ||
551 | else if (!(prot & (IOMMU_READ | IOMMU_WRITE))) | ||
552 | pteval &= ~ARM_VMSA_PTE_PAGE; | ||
553 | |||
554 | pteval |= ARM_VMSA_PTE_SH_IS; | ||
555 | start = pte; | ||
556 | |||
557 | /* Install the page table entries. */ | ||
558 | do { | ||
559 | *pte++ = pfn_pte(pfn++, __pgprot(pteval)); | ||
560 | addr += PAGE_SIZE; | ||
561 | } while (addr != end); | ||
562 | |||
563 | ipmmu_flush_pgtable(mmu, start, sizeof(*pte) * (pte - start)); | ||
564 | return 0; | ||
565 | } | ||
566 | |||
567 | static int ipmmu_alloc_init_pmd(struct ipmmu_vmsa_device *mmu, pud_t *pud, | ||
568 | unsigned long addr, unsigned long end, | ||
569 | phys_addr_t phys, int prot) | ||
570 | { | ||
571 | unsigned long next; | ||
572 | pmd_t *pmd; | ||
573 | int ret; | ||
574 | |||
575 | #ifndef __PAGETABLE_PMD_FOLDED | ||
576 | if (pud_none(*pud)) { | ||
577 | pmd = (pmd_t *)get_zeroed_page(GFP_ATOMIC); | ||
578 | if (!pmd) | ||
579 | return -ENOMEM; | ||
580 | |||
581 | ipmmu_flush_pgtable(mmu, pmd, PAGE_SIZE); | ||
582 | *pud = __pud(__pa(pmd) | PMD_NSTABLE | PMD_TYPE_TABLE); | ||
583 | ipmmu_flush_pgtable(mmu, pud, sizeof(*pud)); | ||
584 | |||
585 | pmd += pmd_index(addr); | ||
586 | } else | ||
587 | #endif | ||
588 | pmd = pmd_offset(pud, addr); | ||
589 | |||
590 | do { | ||
591 | next = pmd_addr_end(addr, end); | ||
592 | ret = ipmmu_alloc_init_pte(mmu, pmd, addr, end, phys, prot); | ||
593 | phys += next - addr; | ||
594 | } while (pmd++, addr = next, addr < end); | ||
595 | |||
596 | return ret; | ||
597 | } | ||
598 | |||
599 | static int ipmmu_alloc_init_pud(struct ipmmu_vmsa_device *mmu, pgd_t *pgd, | ||
600 | unsigned long addr, unsigned long end, | ||
601 | phys_addr_t phys, int prot) | ||
602 | { | ||
603 | unsigned long next; | ||
604 | pud_t *pud; | ||
605 | int ret; | ||
606 | |||
607 | #ifndef __PAGETABLE_PUD_FOLDED | ||
608 | if (pgd_none(*pgd)) { | ||
609 | pud = (pud_t *)get_zeroed_page(GFP_ATOMIC); | ||
610 | if (!pud) | ||
611 | return -ENOMEM; | ||
612 | |||
613 | ipmmu_flush_pgtable(mmu, pud, PAGE_SIZE); | ||
614 | *pgd = __pgd(__pa(pud) | PMD_NSTABLE | PMD_TYPE_TABLE); | ||
615 | ipmmu_flush_pgtable(mmu, pgd, sizeof(*pgd)); | ||
616 | |||
617 | pud += pud_index(addr); | ||
618 | } else | ||
619 | #endif | ||
620 | pud = pud_offset(pgd, addr); | ||
621 | |||
622 | do { | ||
623 | next = pud_addr_end(addr, end); | ||
624 | ret = ipmmu_alloc_init_pmd(mmu, pud, addr, next, phys, prot); | ||
625 | phys += next - addr; | ||
626 | } while (pud++, addr = next, addr < end); | ||
627 | |||
628 | return ret; | ||
629 | } | ||
630 | |||
631 | static int ipmmu_handle_mapping(struct ipmmu_vmsa_domain *domain, | ||
632 | unsigned long iova, phys_addr_t paddr, | ||
633 | size_t size, int prot) | ||
634 | { | ||
635 | struct ipmmu_vmsa_device *mmu = domain->mmu; | ||
636 | pgd_t *pgd = domain->pgd; | ||
637 | unsigned long flags; | ||
638 | unsigned long end; | ||
639 | int ret; | ||
640 | |||
641 | if (!pgd) | ||
642 | return -EINVAL; | ||
643 | |||
644 | if (size & ~PAGE_MASK) | ||
645 | return -EINVAL; | ||
646 | |||
647 | if (paddr & ~((1ULL << 40) - 1)) | ||
648 | return -ERANGE; | ||
649 | |||
650 | spin_lock_irqsave(&domain->lock, flags); | ||
651 | |||
652 | pgd += pgd_index(iova); | ||
653 | end = iova + size; | ||
654 | |||
655 | do { | ||
656 | unsigned long next = pgd_addr_end(iova, end); | ||
657 | |||
658 | ret = ipmmu_alloc_init_pud(mmu, pgd, iova, next, paddr, prot); | ||
659 | if (ret) | ||
660 | break; | ||
661 | |||
662 | paddr += next - iova; | ||
663 | iova = next; | ||
664 | } while (pgd++, iova != end); | ||
665 | |||
666 | spin_unlock_irqrestore(&domain->lock, flags); | ||
667 | |||
668 | ipmmu_tlb_invalidate(domain); | ||
669 | |||
670 | return ret; | ||
671 | } | ||
672 | |||
673 | /* ----------------------------------------------------------------------------- | ||
674 | * IOMMU Operations | ||
675 | */ | ||
676 | |||
677 | static const struct ipmmu_vmsa_master * | ||
678 | ipmmu_find_master(struct ipmmu_vmsa_device *ipmmu, struct device *dev) | ||
679 | { | ||
680 | const struct ipmmu_vmsa_master *master = ipmmu->pdata->masters; | ||
681 | const char *devname = dev_name(dev); | ||
682 | unsigned int i; | ||
683 | |||
684 | for (i = 0; i < ipmmu->pdata->num_masters; ++i, ++master) { | ||
685 | if (strcmp(master->name, devname) == 0) | ||
686 | return master; | ||
687 | } | ||
688 | |||
689 | return NULL; | ||
690 | } | ||
691 | |||
692 | static int ipmmu_domain_init(struct iommu_domain *io_domain) | ||
693 | { | ||
694 | struct ipmmu_vmsa_domain *domain; | ||
695 | |||
696 | domain = kzalloc(sizeof(*domain), GFP_KERNEL); | ||
697 | if (!domain) | ||
698 | return -ENOMEM; | ||
699 | |||
700 | spin_lock_init(&domain->lock); | ||
701 | |||
702 | domain->pgd = kzalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL); | ||
703 | if (!domain->pgd) { | ||
704 | kfree(domain); | ||
705 | return -ENOMEM; | ||
706 | } | ||
707 | |||
708 | io_domain->priv = domain; | ||
709 | domain->io_domain = io_domain; | ||
710 | |||
711 | return 0; | ||
712 | } | ||
713 | |||
714 | static void ipmmu_domain_destroy(struct iommu_domain *io_domain) | ||
715 | { | ||
716 | struct ipmmu_vmsa_domain *domain = io_domain->priv; | ||
717 | |||
718 | /* | ||
719 | * Free the domain resources. We assume that all devices have already | ||
720 | * been detached. | ||
721 | */ | ||
722 | ipmmu_domain_destroy_context(domain); | ||
723 | ipmmu_free_pgtables(domain); | ||
724 | kfree(domain); | ||
725 | } | ||
726 | |||
727 | static int ipmmu_attach_device(struct iommu_domain *io_domain, | ||
728 | struct device *dev) | ||
729 | { | ||
730 | struct ipmmu_vmsa_device *mmu = dev->archdata.iommu; | ||
731 | struct ipmmu_vmsa_domain *domain = io_domain->priv; | ||
732 | const struct ipmmu_vmsa_master *master; | ||
733 | unsigned long flags; | ||
734 | int ret = 0; | ||
735 | |||
736 | if (!mmu) { | ||
737 | dev_err(dev, "Cannot attach to IPMMU\n"); | ||
738 | return -ENXIO; | ||
739 | } | ||
740 | |||
741 | spin_lock_irqsave(&domain->lock, flags); | ||
742 | |||
743 | if (!domain->mmu) { | ||
744 | /* The domain hasn't been used yet, initialize it. */ | ||
745 | domain->mmu = mmu; | ||
746 | ret = ipmmu_domain_init_context(domain); | ||
747 | } else if (domain->mmu != mmu) { | ||
748 | /* | ||
749 | * Something is wrong, we can't attach two devices using | ||
750 | * different IOMMUs to the same domain. | ||
751 | */ | ||
752 | dev_err(dev, "Can't attach IPMMU %s to domain on IPMMU %s\n", | ||
753 | dev_name(mmu->dev), dev_name(domain->mmu->dev)); | ||
754 | ret = -EINVAL; | ||
755 | } | ||
756 | |||
757 | spin_unlock_irqrestore(&domain->lock, flags); | ||
758 | |||
759 | if (ret < 0) | ||
760 | return ret; | ||
761 | |||
762 | master = ipmmu_find_master(mmu, dev); | ||
763 | if (!master) | ||
764 | return -EINVAL; | ||
765 | |||
766 | ipmmu_utlb_enable(domain, master); | ||
767 | |||
768 | return 0; | ||
769 | } | ||
770 | |||
771 | static void ipmmu_detach_device(struct iommu_domain *io_domain, | ||
772 | struct device *dev) | ||
773 | { | ||
774 | struct ipmmu_vmsa_domain *domain = io_domain->priv; | ||
775 | const struct ipmmu_vmsa_master *master; | ||
776 | |||
777 | master = ipmmu_find_master(domain->mmu, dev); | ||
778 | if (!master) | ||
779 | return; | ||
780 | |||
781 | ipmmu_utlb_disable(domain, master); | ||
782 | |||
783 | /* | ||
784 | * TODO: Optimize by disabling the context when no device is attached. | ||
785 | */ | ||
786 | } | ||
787 | |||
788 | static int ipmmu_map(struct iommu_domain *io_domain, unsigned long iova, | ||
789 | phys_addr_t paddr, size_t size, int prot) | ||
790 | { | ||
791 | struct ipmmu_vmsa_domain *domain = io_domain->priv; | ||
792 | |||
793 | if (!domain) | ||
794 | return -ENODEV; | ||
795 | |||
796 | return ipmmu_handle_mapping(domain, iova, paddr, size, prot); | ||
797 | } | ||
798 | |||
799 | static size_t ipmmu_unmap(struct iommu_domain *io_domain, unsigned long iova, | ||
800 | size_t size) | ||
801 | { | ||
802 | struct ipmmu_vmsa_domain *domain = io_domain->priv; | ||
803 | int ret; | ||
804 | |||
805 | ret = ipmmu_handle_mapping(domain, iova, 0, size, 0); | ||
806 | return ret ? 0 : size; | ||
807 | } | ||
808 | |||
809 | static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain, | ||
810 | dma_addr_t iova) | ||
811 | { | ||
812 | struct ipmmu_vmsa_domain *domain = io_domain->priv; | ||
813 | pgd_t pgd; | ||
814 | pud_t pud; | ||
815 | pmd_t pmd; | ||
816 | pte_t pte; | ||
817 | |||
818 | /* TODO: Is locking needed ? */ | ||
819 | |||
820 | if (!domain->pgd) | ||
821 | return 0; | ||
822 | |||
823 | pgd = *(domain->pgd + pgd_index(iova)); | ||
824 | if (pgd_none(pgd)) | ||
825 | return 0; | ||
826 | |||
827 | pud = *pud_offset(&pgd, iova); | ||
828 | if (pud_none(pud)) | ||
829 | return 0; | ||
830 | |||
831 | pmd = *pmd_offset(&pud, iova); | ||
832 | if (pmd_none(pmd)) | ||
833 | return 0; | ||
834 | |||
835 | pte = *(pmd_page_vaddr(pmd) + pte_index(iova)); | ||
836 | if (pte_none(pte)) | ||
837 | return 0; | ||
838 | |||
839 | return __pfn_to_phys(pte_pfn(pte)) | (iova & ~PAGE_MASK); | ||
840 | } | ||
841 | |||
842 | static int ipmmu_add_device(struct device *dev) | ||
843 | { | ||
844 | const struct ipmmu_vmsa_master *master = NULL; | ||
845 | struct ipmmu_vmsa_device *mmu; | ||
846 | struct iommu_group *group; | ||
847 | int ret; | ||
848 | |||
849 | if (dev->archdata.iommu) { | ||
850 | dev_warn(dev, "IOMMU driver already assigned to device %s\n", | ||
851 | dev_name(dev)); | ||
852 | return -EINVAL; | ||
853 | } | ||
854 | |||
855 | /* Find the master corresponding to the device. */ | ||
856 | spin_lock(&ipmmu_devices_lock); | ||
857 | |||
858 | list_for_each_entry(mmu, &ipmmu_devices, list) { | ||
859 | master = ipmmu_find_master(mmu, dev); | ||
860 | if (master) { | ||
861 | /* | ||
862 | * TODO Take a reference to the master to protect | ||
863 | * against device removal. | ||
864 | */ | ||
865 | break; | ||
866 | } | ||
867 | } | ||
868 | |||
869 | spin_unlock(&ipmmu_devices_lock); | ||
870 | |||
871 | if (!master) | ||
872 | return -ENODEV; | ||
873 | |||
874 | if (!master->utlb >= mmu->num_utlbs) | ||
875 | return -EINVAL; | ||
876 | |||
877 | /* Create a device group and add the device to it. */ | ||
878 | group = iommu_group_alloc(); | ||
879 | if (IS_ERR(group)) { | ||
880 | dev_err(dev, "Failed to allocate IOMMU group\n"); | ||
881 | return PTR_ERR(group); | ||
882 | } | ||
883 | |||
884 | ret = iommu_group_add_device(group, dev); | ||
885 | iommu_group_put(group); | ||
886 | |||
887 | if (ret < 0) { | ||
888 | dev_err(dev, "Failed to add device to IPMMU group\n"); | ||
889 | return ret; | ||
890 | } | ||
891 | |||
892 | dev->archdata.iommu = mmu; | ||
893 | |||
894 | /* | ||
895 | * Create the ARM mapping, used by the ARM DMA mapping core to allocate | ||
896 | * VAs. This will allocate a corresponding IOMMU domain. | ||
897 | * | ||
898 | * TODO: | ||
899 | * - Create one mapping per context (TLB). | ||
900 | * - Make the mapping size configurable ? We currently use a 2GB mapping | ||
901 | * at a 1GB offset to ensure that NULL VAs will fault. | ||
902 | */ | ||
903 | if (!mmu->mapping) { | ||
904 | struct dma_iommu_mapping *mapping; | ||
905 | |||
906 | mapping = arm_iommu_create_mapping(&platform_bus_type, | ||
907 | SZ_1G, SZ_2G, 0); | ||
908 | if (IS_ERR(mapping)) { | ||
909 | dev_err(mmu->dev, "failed to create ARM IOMMU mapping\n"); | ||
910 | return PTR_ERR(mapping); | ||
911 | } | ||
912 | |||
913 | mmu->mapping = mapping; | ||
914 | } | ||
915 | |||
916 | /* Attach the ARM VA mapping to the device. */ | ||
917 | ret = arm_iommu_attach_device(dev, mmu->mapping); | ||
918 | if (ret < 0) { | ||
919 | dev_err(dev, "Failed to attach device to VA mapping\n"); | ||
920 | goto error; | ||
921 | } | ||
922 | |||
923 | return 0; | ||
924 | |||
925 | error: | ||
926 | dev->archdata.iommu = NULL; | ||
927 | iommu_group_remove_device(dev); | ||
928 | return ret; | ||
929 | } | ||
930 | |||
931 | static void ipmmu_remove_device(struct device *dev) | ||
932 | { | ||
933 | arm_iommu_detach_device(dev); | ||
934 | iommu_group_remove_device(dev); | ||
935 | dev->archdata.iommu = NULL; | ||
936 | } | ||
937 | |||
938 | static struct iommu_ops ipmmu_ops = { | ||
939 | .domain_init = ipmmu_domain_init, | ||
940 | .domain_destroy = ipmmu_domain_destroy, | ||
941 | .attach_dev = ipmmu_attach_device, | ||
942 | .detach_dev = ipmmu_detach_device, | ||
943 | .map = ipmmu_map, | ||
944 | .unmap = ipmmu_unmap, | ||
945 | .iova_to_phys = ipmmu_iova_to_phys, | ||
946 | .add_device = ipmmu_add_device, | ||
947 | .remove_device = ipmmu_remove_device, | ||
948 | .pgsize_bitmap = SZ_1M | SZ_64K | SZ_4K, | ||
949 | }; | ||
950 | |||
951 | /* ----------------------------------------------------------------------------- | ||
952 | * Probe/remove and init | ||
953 | */ | ||
954 | |||
955 | static void ipmmu_device_reset(struct ipmmu_vmsa_device *mmu) | ||
956 | { | ||
957 | unsigned int i; | ||
958 | |||
959 | /* Disable all contexts. */ | ||
960 | for (i = 0; i < 4; ++i) | ||
961 | ipmmu_write(mmu, i * IM_CTX_SIZE + IMCTR, 0); | ||
962 | } | ||
963 | |||
964 | static int ipmmu_probe(struct platform_device *pdev) | ||
965 | { | ||
966 | struct ipmmu_vmsa_device *mmu; | ||
967 | struct resource *res; | ||
968 | int irq; | ||
969 | int ret; | ||
970 | |||
971 | if (!pdev->dev.platform_data) { | ||
972 | dev_err(&pdev->dev, "missing platform data\n"); | ||
973 | return -EINVAL; | ||
974 | } | ||
975 | |||
976 | mmu = devm_kzalloc(&pdev->dev, sizeof(*mmu), GFP_KERNEL); | ||
977 | if (!mmu) { | ||
978 | dev_err(&pdev->dev, "cannot allocate device data\n"); | ||
979 | return -ENOMEM; | ||
980 | } | ||
981 | |||
982 | mmu->dev = &pdev->dev; | ||
983 | mmu->pdata = pdev->dev.platform_data; | ||
984 | mmu->num_utlbs = 32; | ||
985 | |||
986 | /* Map I/O memory and request IRQ. */ | ||
987 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
988 | mmu->base = devm_ioremap_resource(&pdev->dev, res); | ||
989 | if (IS_ERR(mmu->base)) | ||
990 | return PTR_ERR(mmu->base); | ||
991 | |||
992 | irq = platform_get_irq(pdev, 0); | ||
993 | if (irq < 0) { | ||
994 | dev_err(&pdev->dev, "no IRQ found\n"); | ||
995 | return irq; | ||
996 | } | ||
997 | |||
998 | ret = devm_request_irq(&pdev->dev, irq, ipmmu_irq, 0, | ||
999 | dev_name(&pdev->dev), mmu); | ||
1000 | if (ret < 0) { | ||
1001 | dev_err(&pdev->dev, "failed to request IRQ %d\n", irq); | ||
1002 | return irq; | ||
1003 | } | ||
1004 | |||
1005 | ipmmu_device_reset(mmu); | ||
1006 | |||
1007 | /* | ||
1008 | * We can't create the ARM mapping here as it requires the bus to have | ||
1009 | * an IOMMU, which only happens when bus_set_iommu() is called in | ||
1010 | * ipmmu_init() after the probe function returns. | ||
1011 | */ | ||
1012 | |||
1013 | spin_lock(&ipmmu_devices_lock); | ||
1014 | list_add(&mmu->list, &ipmmu_devices); | ||
1015 | spin_unlock(&ipmmu_devices_lock); | ||
1016 | |||
1017 | platform_set_drvdata(pdev, mmu); | ||
1018 | |||
1019 | return 0; | ||
1020 | } | ||
1021 | |||
1022 | static int ipmmu_remove(struct platform_device *pdev) | ||
1023 | { | ||
1024 | struct ipmmu_vmsa_device *mmu = platform_get_drvdata(pdev); | ||
1025 | |||
1026 | spin_lock(&ipmmu_devices_lock); | ||
1027 | list_del(&mmu->list); | ||
1028 | spin_unlock(&ipmmu_devices_lock); | ||
1029 | |||
1030 | arm_iommu_release_mapping(mmu->mapping); | ||
1031 | |||
1032 | ipmmu_device_reset(mmu); | ||
1033 | |||
1034 | return 0; | ||
1035 | } | ||
1036 | |||
1037 | static struct platform_driver ipmmu_driver = { | ||
1038 | .driver = { | ||
1039 | .owner = THIS_MODULE, | ||
1040 | .name = "ipmmu-vmsa", | ||
1041 | }, | ||
1042 | .probe = ipmmu_probe, | ||
1043 | .remove = ipmmu_remove, | ||
1044 | }; | ||
1045 | |||
1046 | static int __init ipmmu_init(void) | ||
1047 | { | ||
1048 | int ret; | ||
1049 | |||
1050 | ret = platform_driver_register(&ipmmu_driver); | ||
1051 | if (ret < 0) | ||
1052 | return ret; | ||
1053 | |||
1054 | if (!iommu_present(&platform_bus_type)) | ||
1055 | bus_set_iommu(&platform_bus_type, &ipmmu_ops); | ||
1056 | |||
1057 | return 0; | ||
1058 | } | ||
1059 | |||
1060 | static void __exit ipmmu_exit(void) | ||
1061 | { | ||
1062 | return platform_driver_unregister(&ipmmu_driver); | ||
1063 | } | ||
1064 | |||
1065 | subsys_initcall(ipmmu_init); | ||
1066 | module_exit(ipmmu_exit); | ||
1067 | |||
1068 | MODULE_DESCRIPTION("IOMMU API for Renesas VMSA-compatible IPMMU"); | ||
1069 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); | ||
1070 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/include/linux/platform_data/ipmmu-vmsa.h b/include/linux/platform_data/ipmmu-vmsa.h new file mode 100644 index 000000000000..5275b3ac6d37 --- /dev/null +++ b/include/linux/platform_data/ipmmu-vmsa.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * IPMMU VMSA Platform Data | ||
3 | * | ||
4 | * Copyright (C) 2014 Renesas Electronics Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 of the License. | ||
9 | */ | ||
10 | |||
11 | #ifndef __IPMMU_VMSA_H__ | ||
12 | #define __IPMMU_VMSA_H__ | ||
13 | |||
14 | struct ipmmu_vmsa_master { | ||
15 | const char *name; | ||
16 | unsigned int utlb; | ||
17 | }; | ||
18 | |||
19 | struct ipmmu_vmsa_platform_data { | ||
20 | const struct ipmmu_vmsa_master *masters; | ||
21 | unsigned int num_masters; | ||
22 | }; | ||
23 | |||
24 | #endif /* __IPMMU_VMSA_H__ */ | ||