blob: cde3cd0f8c0c0f05e8e95e59887edcdf2216e0b6 [file] [log] [blame]
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -08001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/platform_device.h>
22#include <linux/errno.h>
23#include <linux/io.h>
24#include <linux/interrupt.h>
25#include <linux/list.h>
26#include <linux/spinlock.h>
27#include <linux/slab.h>
28#include <linux/iommu.h>
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -080029#include <linux/clk.h>
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -070030
31#include <asm/cacheflush.h>
32#include <asm/sizes.h>
33
34#include <mach/iommu_hw-8xxx.h>
35#include <mach/iommu.h>
36
Stepan Moskovchenko100832c2010-11-15 18:20:08 -080037#define MRC(reg, processor, op1, crn, crm, op2) \
38__asm__ __volatile__ ( \
39" mrc " #processor "," #op1 ", %0," #crn "," #crm "," #op2 "\n" \
40: "=r" (reg))
41
42#define RCP15_PRRR(reg) MRC(reg, p15, 0, c10, c2, 0)
43#define RCP15_NMRR(reg) MRC(reg, p15, 0, c10, c2, 1)
44
45static int msm_iommu_tex_class[4];
46
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -070047DEFINE_SPINLOCK(msm_iommu_lock);
48
49struct msm_priv {
50 unsigned long *pgtable;
51 struct list_head list_attached;
52};
53
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -080054static int __enable_clocks(struct msm_iommu_drvdata *drvdata)
55{
56 int ret;
57
58 ret = clk_enable(drvdata->pclk);
59 if (ret)
60 goto fail;
61
62 if (drvdata->clk) {
63 ret = clk_enable(drvdata->clk);
64 if (ret)
65 clk_disable(drvdata->pclk);
66 }
67fail:
68 return ret;
69}
70
71static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
72{
73 if (drvdata->clk)
74 clk_disable(drvdata->clk);
75 clk_disable(drvdata->pclk);
76}
77
Stepan Moskovchenko33069732010-11-12 19:30:00 -080078static int __flush_iotlb(struct iommu_domain *domain)
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -070079{
80 struct msm_priv *priv = domain->priv;
81 struct msm_iommu_drvdata *iommu_drvdata;
82 struct msm_iommu_ctx_drvdata *ctx_drvdata;
Stepan Moskovchenko33069732010-11-12 19:30:00 -080083 int ret = 0;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -070084#ifndef CONFIG_IOMMU_PGTABLES_L2
85 unsigned long *fl_table = priv->pgtable;
86 int i;
87
Stepan Moskovchenkof6f41eb2010-11-12 19:29:54 -080088 if (!list_empty(&priv->list_attached)) {
89 dmac_flush_range(fl_table, fl_table + SZ_16K);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -070090
Stepan Moskovchenkof6f41eb2010-11-12 19:29:54 -080091 for (i = 0; i < NUM_FL_PTE; i++)
92 if ((fl_table[i] & 0x03) == FL_TYPE_TABLE) {
93 void *sl_table = __va(fl_table[i] &
94 FL_BASE_MASK);
95 dmac_flush_range(sl_table, sl_table + SZ_4K);
96 }
97 }
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -070098#endif
99
100 list_for_each_entry(ctx_drvdata, &priv->list_attached, attached_elm) {
101 if (!ctx_drvdata->pdev || !ctx_drvdata->pdev->dev.parent)
102 BUG();
103
104 iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800105 BUG_ON(!iommu_drvdata);
Stepan Moskovchenko33069732010-11-12 19:30:00 -0800106
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800107 ret = __enable_clocks(iommu_drvdata);
108 if (ret)
109 goto fail;
110
111 SET_CTX_TLBIALL(iommu_drvdata->base, ctx_drvdata->num, 0);
112 __disable_clocks(iommu_drvdata);
113 }
114fail:
Stepan Moskovchenko33069732010-11-12 19:30:00 -0800115 return ret;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700116}
117
118static void __reset_context(void __iomem *base, int ctx)
119{
120 SET_BPRCOSH(base, ctx, 0);
121 SET_BPRCISH(base, ctx, 0);
122 SET_BPRCNSH(base, ctx, 0);
123 SET_BPSHCFG(base, ctx, 0);
124 SET_BPMTCFG(base, ctx, 0);
125 SET_ACTLR(base, ctx, 0);
126 SET_SCTLR(base, ctx, 0);
127 SET_FSRRESTORE(base, ctx, 0);
128 SET_TTBR0(base, ctx, 0);
129 SET_TTBR1(base, ctx, 0);
130 SET_TTBCR(base, ctx, 0);
131 SET_BFBCR(base, ctx, 0);
132 SET_PAR(base, ctx, 0);
133 SET_FAR(base, ctx, 0);
134 SET_CTX_TLBIALL(base, ctx, 0);
135 SET_TLBFLPTER(base, ctx, 0);
136 SET_TLBSLPTER(base, ctx, 0);
137 SET_TLBLKCR(base, ctx, 0);
138 SET_PRRR(base, ctx, 0);
139 SET_NMRR(base, ctx, 0);
140 SET_CONTEXTIDR(base, ctx, 0);
141}
142
143static void __program_context(void __iomem *base, int ctx, phys_addr_t pgtable)
144{
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800145 unsigned int prrr, nmrr;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700146 __reset_context(base, ctx);
147
148 /* Set up HTW mode */
149 /* TLB miss configuration: perform HTW on miss */
150 SET_TLBMCFG(base, ctx, 0x3);
151
152 /* V2P configuration: HTW for access */
153 SET_V2PCFG(base, ctx, 0x3);
154
155 SET_TTBCR(base, ctx, 0);
156 SET_TTBR0_PA(base, ctx, (pgtable >> 14));
157
158 /* Invalidate the TLB for this context */
159 SET_CTX_TLBIALL(base, ctx, 0);
160
161 /* Set interrupt number to "secure" interrupt */
162 SET_IRPTNDX(base, ctx, 0);
163
164 /* Enable context fault interrupt */
165 SET_CFEIE(base, ctx, 1);
166
167 /* Stall access on a context fault and let the handler deal with it */
168 SET_CFCFG(base, ctx, 1);
169
170 /* Redirect all cacheable requests to L2 slave port. */
171 SET_RCISH(base, ctx, 1);
172 SET_RCOSH(base, ctx, 1);
173 SET_RCNSH(base, ctx, 1);
174
175 /* Turn on TEX Remap */
176 SET_TRE(base, ctx, 1);
177
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800178 /* Set TEX remap attributes */
179 RCP15_PRRR(prrr);
180 RCP15_NMRR(nmrr);
181 SET_PRRR(base, ctx, prrr);
182 SET_NMRR(base, ctx, nmrr);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700183
184 /* Turn on BFB prefetch */
185 SET_BFBDFE(base, ctx, 1);
186
187#ifdef CONFIG_IOMMU_PGTABLES_L2
188 /* Configure page tables as inner-cacheable and shareable to reduce
189 * the TLB miss penalty.
190 */
191 SET_TTBR0_SH(base, ctx, 1);
192 SET_TTBR1_SH(base, ctx, 1);
193
194 SET_TTBR0_NOS(base, ctx, 1);
195 SET_TTBR1_NOS(base, ctx, 1);
196
197 SET_TTBR0_IRGNH(base, ctx, 0); /* WB, WA */
198 SET_TTBR0_IRGNL(base, ctx, 1);
199
200 SET_TTBR1_IRGNH(base, ctx, 0); /* WB, WA */
201 SET_TTBR1_IRGNL(base, ctx, 1);
202
203 SET_TTBR0_ORGN(base, ctx, 1); /* WB, WA */
204 SET_TTBR1_ORGN(base, ctx, 1); /* WB, WA */
205#endif
206
207 /* Enable the MMU */
208 SET_M(base, ctx, 1);
209}
210
211static int msm_iommu_domain_init(struct iommu_domain *domain)
212{
213 struct msm_priv *priv = kzalloc(sizeof(*priv), GFP_KERNEL);
214
215 if (!priv)
216 goto fail_nomem;
217
218 INIT_LIST_HEAD(&priv->list_attached);
219 priv->pgtable = (unsigned long *)__get_free_pages(GFP_KERNEL,
220 get_order(SZ_16K));
221
222 if (!priv->pgtable)
223 goto fail_nomem;
224
225 memset(priv->pgtable, 0, SZ_16K);
226 domain->priv = priv;
227 return 0;
228
229fail_nomem:
230 kfree(priv);
231 return -ENOMEM;
232}
233
234static void msm_iommu_domain_destroy(struct iommu_domain *domain)
235{
236 struct msm_priv *priv;
237 unsigned long flags;
238 unsigned long *fl_table;
239 int i;
240
241 spin_lock_irqsave(&msm_iommu_lock, flags);
242 priv = domain->priv;
243 domain->priv = NULL;
244
245 if (priv) {
246 fl_table = priv->pgtable;
247
248 for (i = 0; i < NUM_FL_PTE; i++)
249 if ((fl_table[i] & 0x03) == FL_TYPE_TABLE)
250 free_page((unsigned long) __va(((fl_table[i]) &
251 FL_BASE_MASK)));
252
253 free_pages((unsigned long)priv->pgtable, get_order(SZ_16K));
254 priv->pgtable = NULL;
255 }
256
257 kfree(priv);
258 spin_unlock_irqrestore(&msm_iommu_lock, flags);
259}
260
261static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
262{
263 struct msm_priv *priv;
264 struct msm_iommu_ctx_dev *ctx_dev;
265 struct msm_iommu_drvdata *iommu_drvdata;
266 struct msm_iommu_ctx_drvdata *ctx_drvdata;
267 struct msm_iommu_ctx_drvdata *tmp_drvdata;
268 int ret = 0;
269 unsigned long flags;
270
271 spin_lock_irqsave(&msm_iommu_lock, flags);
272
273 priv = domain->priv;
274
275 if (!priv || !dev) {
276 ret = -EINVAL;
277 goto fail;
278 }
279
280 iommu_drvdata = dev_get_drvdata(dev->parent);
281 ctx_drvdata = dev_get_drvdata(dev);
282 ctx_dev = dev->platform_data;
283
284 if (!iommu_drvdata || !ctx_drvdata || !ctx_dev) {
285 ret = -EINVAL;
286 goto fail;
287 }
288
Stepan Moskovchenko00d4b2b2010-11-12 19:29:56 -0800289 if (!list_empty(&ctx_drvdata->attached_elm)) {
290 ret = -EBUSY;
291 goto fail;
292 }
293
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700294 list_for_each_entry(tmp_drvdata, &priv->list_attached, attached_elm)
295 if (tmp_drvdata == ctx_drvdata) {
296 ret = -EBUSY;
297 goto fail;
298 }
299
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800300 ret = __enable_clocks(iommu_drvdata);
301 if (ret)
302 goto fail;
303
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700304 __program_context(iommu_drvdata->base, ctx_dev->num,
305 __pa(priv->pgtable));
306
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800307 __disable_clocks(iommu_drvdata);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700308 list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
Stepan Moskovchenko33069732010-11-12 19:30:00 -0800309 ret = __flush_iotlb(domain);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700310
311fail:
312 spin_unlock_irqrestore(&msm_iommu_lock, flags);
313 return ret;
314}
315
316static void msm_iommu_detach_dev(struct iommu_domain *domain,
317 struct device *dev)
318{
319 struct msm_priv *priv;
320 struct msm_iommu_ctx_dev *ctx_dev;
321 struct msm_iommu_drvdata *iommu_drvdata;
322 struct msm_iommu_ctx_drvdata *ctx_drvdata;
323 unsigned long flags;
Stepan Moskovchenko33069732010-11-12 19:30:00 -0800324 int ret;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700325
326 spin_lock_irqsave(&msm_iommu_lock, flags);
327 priv = domain->priv;
328
329 if (!priv || !dev)
330 goto fail;
331
332 iommu_drvdata = dev_get_drvdata(dev->parent);
333 ctx_drvdata = dev_get_drvdata(dev);
334 ctx_dev = dev->platform_data;
335
336 if (!iommu_drvdata || !ctx_drvdata || !ctx_dev)
337 goto fail;
338
Stepan Moskovchenko33069732010-11-12 19:30:00 -0800339 ret = __flush_iotlb(domain);
340 if (ret)
341 goto fail;
342
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800343 ret = __enable_clocks(iommu_drvdata);
344 if (ret)
345 goto fail;
346
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700347 __reset_context(iommu_drvdata->base, ctx_dev->num);
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800348 __disable_clocks(iommu_drvdata);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700349 list_del_init(&ctx_drvdata->attached_elm);
350
351fail:
352 spin_unlock_irqrestore(&msm_iommu_lock, flags);
353}
354
355static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
356 phys_addr_t pa, int order, int prot)
357{
358 struct msm_priv *priv;
359 unsigned long flags;
360 unsigned long *fl_table;
361 unsigned long *fl_pte;
362 unsigned long fl_offset;
363 unsigned long *sl_table;
364 unsigned long *sl_pte;
365 unsigned long sl_offset;
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800366 unsigned int pgprot;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700367 size_t len = 0x1000UL << order;
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800368 int ret = 0, tex, sh;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700369
370 spin_lock_irqsave(&msm_iommu_lock, flags);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700371
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800372 sh = (prot & MSM_IOMMU_ATTR_SH) ? 1 : 0;
373 tex = msm_iommu_tex_class[prot & MSM_IOMMU_CP_MASK];
374
375 if (tex < 0 || tex > NUM_TEX_CLASS - 1) {
376 ret = -EINVAL;
377 goto fail;
378 }
379
380 priv = domain->priv;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700381 if (!priv) {
382 ret = -EINVAL;
383 goto fail;
384 }
385
386 fl_table = priv->pgtable;
387
388 if (len != SZ_16M && len != SZ_1M &&
389 len != SZ_64K && len != SZ_4K) {
390 pr_debug("Bad size: %d\n", len);
391 ret = -EINVAL;
392 goto fail;
393 }
394
395 if (!fl_table) {
396 pr_debug("Null page table\n");
397 ret = -EINVAL;
398 goto fail;
399 }
400
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800401 if (len == SZ_16M || len == SZ_1M) {
402 pgprot = sh ? FL_SHARED : 0;
403 pgprot |= tex & 0x01 ? FL_BUFFERABLE : 0;
404 pgprot |= tex & 0x02 ? FL_CACHEABLE : 0;
405 pgprot |= tex & 0x04 ? FL_TEX0 : 0;
406 } else {
407 pgprot = sh ? SL_SHARED : 0;
408 pgprot |= tex & 0x01 ? SL_BUFFERABLE : 0;
409 pgprot |= tex & 0x02 ? SL_CACHEABLE : 0;
410 pgprot |= tex & 0x04 ? SL_TEX0 : 0;
411 }
412
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700413 fl_offset = FL_OFFSET(va); /* Upper 12 bits */
414 fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
415
416 if (len == SZ_16M) {
417 int i = 0;
418 for (i = 0; i < 16; i++)
419 *(fl_pte+i) = (pa & 0xFF000000) | FL_SUPERSECTION |
420 FL_AP_READ | FL_AP_WRITE | FL_TYPE_SECT |
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800421 FL_SHARED | pgprot;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700422 }
423
424 if (len == SZ_1M)
425 *fl_pte = (pa & 0xFFF00000) | FL_AP_READ | FL_AP_WRITE |
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800426 FL_TYPE_SECT | FL_SHARED | pgprot;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700427
428 /* Need a 2nd level table */
429 if ((len == SZ_4K || len == SZ_64K) && (*fl_pte) == 0) {
430 unsigned long *sl;
Stepan Moskovchenko294b2de2010-12-10 14:12:03 -0800431 sl = (unsigned long *) __get_free_pages(GFP_ATOMIC,
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700432 get_order(SZ_4K));
433
434 if (!sl) {
435 pr_debug("Could not allocate second level table\n");
436 ret = -ENOMEM;
437 goto fail;
438 }
439
440 memset(sl, 0, SZ_4K);
441 *fl_pte = ((((int)__pa(sl)) & FL_BASE_MASK) | FL_TYPE_TABLE);
442 }
443
444 sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
445 sl_offset = SL_OFFSET(va);
446 sl_pte = sl_table + sl_offset;
447
448
449 if (len == SZ_4K)
450 *sl_pte = (pa & SL_BASE_MASK_SMALL) | SL_AP0 | SL_AP1 |
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800451 SL_SHARED | SL_TYPE_SMALL | pgprot;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700452
453 if (len == SZ_64K) {
454 int i;
455
456 for (i = 0; i < 16; i++)
457 *(sl_pte+i) = (pa & SL_BASE_MASK_LARGE) | SL_AP0 |
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800458 SL_AP1 | SL_SHARED | SL_TYPE_LARGE | pgprot;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700459 }
460
Stepan Moskovchenko33069732010-11-12 19:30:00 -0800461 ret = __flush_iotlb(domain);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700462fail:
463 spin_unlock_irqrestore(&msm_iommu_lock, flags);
464 return ret;
465}
466
467static int msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
468 int order)
469{
470 struct msm_priv *priv;
471 unsigned long flags;
472 unsigned long *fl_table;
473 unsigned long *fl_pte;
474 unsigned long fl_offset;
475 unsigned long *sl_table;
476 unsigned long *sl_pte;
477 unsigned long sl_offset;
478 size_t len = 0x1000UL << order;
479 int i, ret = 0;
480
481 spin_lock_irqsave(&msm_iommu_lock, flags);
482
483 priv = domain->priv;
484
485 if (!priv) {
486 ret = -ENODEV;
487 goto fail;
488 }
489
490 fl_table = priv->pgtable;
491
492 if (len != SZ_16M && len != SZ_1M &&
493 len != SZ_64K && len != SZ_4K) {
494 pr_debug("Bad length: %d\n", len);
495 ret = -EINVAL;
496 goto fail;
497 }
498
499 if (!fl_table) {
500 pr_debug("Null page table\n");
501 ret = -EINVAL;
502 goto fail;
503 }
504
505 fl_offset = FL_OFFSET(va); /* Upper 12 bits */
506 fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
507
508 if (*fl_pte == 0) {
509 pr_debug("First level PTE is 0\n");
510 ret = -ENODEV;
511 goto fail;
512 }
513
514 /* Unmap supersection */
515 if (len == SZ_16M)
516 for (i = 0; i < 16; i++)
517 *(fl_pte+i) = 0;
518
519 if (len == SZ_1M)
520 *fl_pte = 0;
521
522 sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
523 sl_offset = SL_OFFSET(va);
524 sl_pte = sl_table + sl_offset;
525
526 if (len == SZ_64K) {
527 for (i = 0; i < 16; i++)
528 *(sl_pte+i) = 0;
529 }
530
531 if (len == SZ_4K)
532 *sl_pte = 0;
533
534 if (len == SZ_4K || len == SZ_64K) {
535 int used = 0;
536
537 for (i = 0; i < NUM_SL_PTE; i++)
538 if (sl_table[i])
539 used = 1;
540 if (!used) {
541 free_page((unsigned long)sl_table);
542 *fl_pte = 0;
543 }
544 }
545
Stepan Moskovchenko33069732010-11-12 19:30:00 -0800546 ret = __flush_iotlb(domain);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700547fail:
548 spin_unlock_irqrestore(&msm_iommu_lock, flags);
549 return ret;
550}
551
552static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain,
553 unsigned long va)
554{
555 struct msm_priv *priv;
556 struct msm_iommu_drvdata *iommu_drvdata;
557 struct msm_iommu_ctx_drvdata *ctx_drvdata;
558 unsigned int par;
559 unsigned long flags;
560 void __iomem *base;
561 phys_addr_t ret = 0;
562 int ctx;
563
564 spin_lock_irqsave(&msm_iommu_lock, flags);
565
566 priv = domain->priv;
567 if (list_empty(&priv->list_attached))
568 goto fail;
569
570 ctx_drvdata = list_entry(priv->list_attached.next,
571 struct msm_iommu_ctx_drvdata, attached_elm);
572 iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
573
574 base = iommu_drvdata->base;
575 ctx = ctx_drvdata->num;
576
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800577 ret = __enable_clocks(iommu_drvdata);
578 if (ret)
579 goto fail;
580
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700581 /* Invalidate context TLB */
582 SET_CTX_TLBIALL(base, ctx, 0);
583 SET_V2PPR_VA(base, ctx, va >> V2Pxx_VA_SHIFT);
584
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700585 par = GET_PAR(base, ctx);
586
587 /* We are dealing with a supersection */
588 if (GET_NOFAULT_SS(base, ctx))
589 ret = (par & 0xFF000000) | (va & 0x00FFFFFF);
590 else /* Upper 20 bits from PAR, lower 12 from VA */
591 ret = (par & 0xFFFFF000) | (va & 0x00000FFF);
592
Stepan Moskovchenko33069732010-11-12 19:30:00 -0800593 if (GET_FAULT(base, ctx))
594 ret = 0;
595
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800596 __disable_clocks(iommu_drvdata);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700597fail:
598 spin_unlock_irqrestore(&msm_iommu_lock, flags);
599 return ret;
600}
601
602static int msm_iommu_domain_has_cap(struct iommu_domain *domain,
603 unsigned long cap)
604{
605 return 0;
606}
607
608static void print_ctx_regs(void __iomem *base, int ctx)
609{
610 unsigned int fsr = GET_FSR(base, ctx);
611 pr_err("FAR = %08x PAR = %08x\n",
612 GET_FAR(base, ctx), GET_PAR(base, ctx));
613 pr_err("FSR = %08x [%s%s%s%s%s%s%s%s%s%s]\n", fsr,
614 (fsr & 0x02) ? "TF " : "",
615 (fsr & 0x04) ? "AFF " : "",
616 (fsr & 0x08) ? "APF " : "",
617 (fsr & 0x10) ? "TLBMF " : "",
618 (fsr & 0x20) ? "HTWDEEF " : "",
619 (fsr & 0x40) ? "HTWSEEF " : "",
620 (fsr & 0x80) ? "MHF " : "",
621 (fsr & 0x10000) ? "SL " : "",
622 (fsr & 0x40000000) ? "SS " : "",
623 (fsr & 0x80000000) ? "MULTI " : "");
624
625 pr_err("FSYNR0 = %08x FSYNR1 = %08x\n",
626 GET_FSYNR0(base, ctx), GET_FSYNR1(base, ctx));
627 pr_err("TTBR0 = %08x TTBR1 = %08x\n",
628 GET_TTBR0(base, ctx), GET_TTBR1(base, ctx));
629 pr_err("SCTLR = %08x ACTLR = %08x\n",
630 GET_SCTLR(base, ctx), GET_ACTLR(base, ctx));
631 pr_err("PRRR = %08x NMRR = %08x\n",
632 GET_PRRR(base, ctx), GET_NMRR(base, ctx));
633}
634
635irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id)
636{
637 struct msm_iommu_drvdata *drvdata = dev_id;
638 void __iomem *base;
Stepan Moskovchenko33069732010-11-12 19:30:00 -0800639 unsigned int fsr;
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800640 int ncb, i, ret;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700641
642 spin_lock(&msm_iommu_lock);
643
644 if (!drvdata) {
645 pr_err("Invalid device ID in context interrupt handler\n");
646 goto fail;
647 }
648
649 base = drvdata->base;
650
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700651 pr_err("Unexpected IOMMU page fault!\n");
652 pr_err("base = %08x\n", (unsigned int) base);
653
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800654 ret = __enable_clocks(drvdata);
655 if (ret)
656 goto fail;
657
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700658 ncb = GET_NCB(base)+1;
659 for (i = 0; i < ncb; i++) {
660 fsr = GET_FSR(base, i);
661 if (fsr) {
662 pr_err("Fault occurred in context %d.\n", i);
663 pr_err("Interesting registers:\n");
664 print_ctx_regs(base, i);
665 SET_FSR(base, i, 0x4000000F);
666 }
667 }
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800668 __disable_clocks(drvdata);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700669fail:
670 spin_unlock(&msm_iommu_lock);
671 return 0;
672}
673
674static struct iommu_ops msm_iommu_ops = {
675 .domain_init = msm_iommu_domain_init,
676 .domain_destroy = msm_iommu_domain_destroy,
677 .attach_dev = msm_iommu_attach_dev,
678 .detach_dev = msm_iommu_detach_dev,
679 .map = msm_iommu_map,
680 .unmap = msm_iommu_unmap,
681 .iova_to_phys = msm_iommu_iova_to_phys,
682 .domain_has_cap = msm_iommu_domain_has_cap
683};
684
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800685static int __init get_tex_class(int icp, int ocp, int mt, int nos)
686{
687 int i = 0;
688 unsigned int prrr = 0;
689 unsigned int nmrr = 0;
690 int c_icp, c_ocp, c_mt, c_nos;
691
692 RCP15_PRRR(prrr);
693 RCP15_NMRR(nmrr);
694
695 for (i = 0; i < NUM_TEX_CLASS; i++) {
696 c_nos = PRRR_NOS(prrr, i);
697 c_mt = PRRR_MT(prrr, i);
698 c_icp = NMRR_ICP(nmrr, i);
699 c_ocp = NMRR_OCP(nmrr, i);
700
701 if (icp == c_icp && ocp == c_ocp && c_mt == mt && c_nos == nos)
702 return i;
703 }
704
705 return -ENODEV;
706}
707
708static void __init setup_iommu_tex_classes(void)
709{
710 msm_iommu_tex_class[MSM_IOMMU_ATTR_NONCACHED] =
711 get_tex_class(CP_NONCACHED, CP_NONCACHED, MT_NORMAL, 1);
712
713 msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WB_WA] =
714 get_tex_class(CP_WB_WA, CP_WB_WA, MT_NORMAL, 1);
715
716 msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WB_NWA] =
717 get_tex_class(CP_WB_NWA, CP_WB_NWA, MT_NORMAL, 1);
718
719 msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WT] =
720 get_tex_class(CP_WT, CP_WT, MT_NORMAL, 1);
721}
722
Stepan Moskovchenko516cbc72010-11-12 19:29:53 -0800723static int __init msm_iommu_init(void)
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700724{
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800725 setup_iommu_tex_classes();
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700726 register_iommu(&msm_iommu_ops);
727 return 0;
728}
729
730subsys_initcall(msm_iommu_init);
731
732MODULE_LICENSE("GPL v2");
733MODULE_AUTHOR("Stepan Moskovchenko <stepanm@codeaurora.org>");