blob: a310ba04b2d8258ffd4789573aea813643d3a160 [file] [log] [blame]
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -08001/* Copyright (c) 2010-2012, 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.
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -070011 */
12
13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/platform_device.h>
17#include <linux/errno.h>
18#include <linux/io.h>
19#include <linux/interrupt.h>
20#include <linux/list.h>
21#include <linux/spinlock.h>
22#include <linux/slab.h>
23#include <linux/iommu.h>
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -080024#include <linux/clk.h>
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -070025#include <linux/scatterlist.h>
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -070026
27#include <asm/cacheflush.h>
28#include <asm/sizes.h>
29
30#include <mach/iommu_hw-8xxx.h>
31#include <mach/iommu.h>
32
Stepan Moskovchenko100832c2010-11-15 18:20:08 -080033#define MRC(reg, processor, op1, crn, crm, op2) \
34__asm__ __volatile__ ( \
35" mrc " #processor "," #op1 ", %0," #crn "," #crm "," #op2 "\n" \
36: "=r" (reg))
37
38#define RCP15_PRRR(reg) MRC(reg, p15, 0, c10, c2, 0)
39#define RCP15_NMRR(reg) MRC(reg, p15, 0, c10, c2, 1)
40
Stepan Moskovchenko6ee3be82011-11-08 15:24:53 -080041/* Sharability attributes of MSM IOMMU mappings */
42#define MSM_IOMMU_ATTR_NON_SH 0x0
43#define MSM_IOMMU_ATTR_SH 0x4
44
45/* Cacheability attributes of MSM IOMMU mappings */
46#define MSM_IOMMU_ATTR_NONCACHED 0x0
47#define MSM_IOMMU_ATTR_CACHED_WB_WA 0x1
48#define MSM_IOMMU_ATTR_CACHED_WB_NWA 0x2
49#define MSM_IOMMU_ATTR_CACHED_WT 0x3
50
51
Stepan Moskovchenko094475d2011-08-03 13:38:29 -070052static inline void clean_pte(unsigned long *start, unsigned long *end)
53{
54 dmac_flush_range(start, end);
55}
Stepan Moskovchenko094475d2011-08-03 13:38:29 -070056
Stepan Moskovchenko100832c2010-11-15 18:20:08 -080057static int msm_iommu_tex_class[4];
58
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -080059DEFINE_MUTEX(msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -070060
61struct msm_priv {
62 unsigned long *pgtable;
Stepan Moskovchenkob2438892011-08-31 17:16:19 -070063 int redirect;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -070064 struct list_head list_attached;
65};
66
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -080067static int __enable_clocks(struct msm_iommu_drvdata *drvdata)
68{
69 int ret;
70
Stepan Moskovchenkobbf95e12012-02-14 15:42:27 -080071 ret = clk_prepare_enable(drvdata->pclk);
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -080072 if (ret)
73 goto fail;
74
75 if (drvdata->clk) {
Stepan Moskovchenkobbf95e12012-02-14 15:42:27 -080076 ret = clk_prepare_enable(drvdata->clk);
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -080077 if (ret)
Stepan Moskovchenkobbf95e12012-02-14 15:42:27 -080078 clk_disable_unprepare(drvdata->pclk);
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -080079 }
80fail:
81 return ret;
82}
83
84static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
85{
86 if (drvdata->clk)
Stepan Moskovchenkobbf95e12012-02-14 15:42:27 -080087 clk_disable_unprepare(drvdata->clk);
88 clk_disable_unprepare(drvdata->pclk);
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -080089}
90
Stepan Moskovchenkobd1ad612011-08-03 16:24:54 -070091static int __flush_iotlb_va(struct iommu_domain *domain, unsigned int va)
92{
93 struct msm_priv *priv = domain->priv;
94 struct msm_iommu_drvdata *iommu_drvdata;
95 struct msm_iommu_ctx_drvdata *ctx_drvdata;
96 int ret = 0;
97 int asid;
98
99 list_for_each_entry(ctx_drvdata, &priv->list_attached, attached_elm) {
100 if (!ctx_drvdata->pdev || !ctx_drvdata->pdev->dev.parent)
101 BUG();
102
103 iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
104 if (!iommu_drvdata)
105 BUG();
106
107 ret = __enable_clocks(iommu_drvdata);
108 if (ret)
109 goto fail;
110
111 asid = GET_CONTEXTIDR_ASID(iommu_drvdata->base,
112 ctx_drvdata->num);
113
114 SET_TLBIVA(iommu_drvdata->base, ctx_drvdata->num,
115 asid | (va & TLBIVA_VA));
116 mb();
117 __disable_clocks(iommu_drvdata);
118 }
119fail:
120 return ret;
121}
122
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700123static int __flush_iotlb(struct iommu_domain *domain)
124{
125 struct msm_priv *priv = domain->priv;
126 struct msm_iommu_drvdata *iommu_drvdata;
127 struct msm_iommu_ctx_drvdata *ctx_drvdata;
128 int ret = 0;
129 int asid;
130
131 list_for_each_entry(ctx_drvdata, &priv->list_attached, attached_elm) {
132 if (!ctx_drvdata->pdev || !ctx_drvdata->pdev->dev.parent)
133 BUG();
134
135 iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
136 if (!iommu_drvdata)
137 BUG();
138
139 ret = __enable_clocks(iommu_drvdata);
140 if (ret)
141 goto fail;
142
143 asid = GET_CONTEXTIDR_ASID(iommu_drvdata->base,
144 ctx_drvdata->num);
145
146 SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num, asid);
147 mb();
148 __disable_clocks(iommu_drvdata);
149 }
150fail:
151 return ret;
152}
153
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700154static void __reset_context(void __iomem *base, int ctx)
155{
156 SET_BPRCOSH(base, ctx, 0);
157 SET_BPRCISH(base, ctx, 0);
158 SET_BPRCNSH(base, ctx, 0);
159 SET_BPSHCFG(base, ctx, 0);
160 SET_BPMTCFG(base, ctx, 0);
161 SET_ACTLR(base, ctx, 0);
162 SET_SCTLR(base, ctx, 0);
163 SET_FSRRESTORE(base, ctx, 0);
164 SET_TTBR0(base, ctx, 0);
165 SET_TTBR1(base, ctx, 0);
166 SET_TTBCR(base, ctx, 0);
167 SET_BFBCR(base, ctx, 0);
168 SET_PAR(base, ctx, 0);
169 SET_FAR(base, ctx, 0);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700170 SET_TLBFLPTER(base, ctx, 0);
171 SET_TLBSLPTER(base, ctx, 0);
172 SET_TLBLKCR(base, ctx, 0);
173 SET_PRRR(base, ctx, 0);
174 SET_NMRR(base, ctx, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175 mb();
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700176}
177
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700178static void __program_context(void __iomem *base, int ctx, int ncb,
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700179 phys_addr_t pgtable, int redirect)
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700180{
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800181 unsigned int prrr, nmrr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700182 int i, j, found;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700183 __reset_context(base, ctx);
184
185 /* Set up HTW mode */
186 /* TLB miss configuration: perform HTW on miss */
187 SET_TLBMCFG(base, ctx, 0x3);
188
189 /* V2P configuration: HTW for access */
190 SET_V2PCFG(base, ctx, 0x3);
191
192 SET_TTBCR(base, ctx, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193 SET_TTBR0_PA(base, ctx, (pgtable >> TTBR0_PA_SHIFT));
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700194
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700195 /* Enable context fault interrupt */
196 SET_CFEIE(base, ctx, 1);
197
198 /* Stall access on a context fault and let the handler deal with it */
199 SET_CFCFG(base, ctx, 1);
200
201 /* Redirect all cacheable requests to L2 slave port. */
202 SET_RCISH(base, ctx, 1);
203 SET_RCOSH(base, ctx, 1);
204 SET_RCNSH(base, ctx, 1);
205
206 /* Turn on TEX Remap */
207 SET_TRE(base, ctx, 1);
208
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800209 /* Set TEX remap attributes */
210 RCP15_PRRR(prrr);
211 RCP15_NMRR(nmrr);
212 SET_PRRR(base, ctx, prrr);
213 SET_NMRR(base, ctx, nmrr);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700214
215 /* Turn on BFB prefetch */
216 SET_BFBDFE(base, ctx, 1);
217
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700218 /* Configure page tables as inner-cacheable and shareable to reduce
219 * the TLB miss penalty.
220 */
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700221 if (redirect) {
222 SET_TTBR0_SH(base, ctx, 1);
223 SET_TTBR1_SH(base, ctx, 1);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700224
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700225 SET_TTBR0_NOS(base, ctx, 1);
226 SET_TTBR1_NOS(base, ctx, 1);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700227
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700228 SET_TTBR0_IRGNH(base, ctx, 0); /* WB, WA */
229 SET_TTBR0_IRGNL(base, ctx, 1);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700230
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700231 SET_TTBR1_IRGNH(base, ctx, 0); /* WB, WA */
232 SET_TTBR1_IRGNL(base, ctx, 1);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700233
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700234 SET_TTBR0_ORGN(base, ctx, 1); /* WB, WA */
235 SET_TTBR1_ORGN(base, ctx, 1); /* WB, WA */
236 }
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700237
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700238 /* Find if this page table is used elsewhere, and re-use ASID */
239 found = 0;
240 for (i = 0; i < ncb; i++)
241 if (GET_TTBR0_PA(base, i) == (pgtable >> TTBR0_PA_SHIFT) &&
242 i != ctx) {
243 SET_CONTEXTIDR_ASID(base, ctx, \
244 GET_CONTEXTIDR_ASID(base, i));
245 found = 1;
246 break;
247 }
248
249 /* If page table is new, find an unused ASID */
250 if (!found) {
251 for (i = 0; i < ncb; i++) {
252 found = 0;
253 for (j = 0; j < ncb; j++) {
254 if (GET_CONTEXTIDR_ASID(base, j) == i &&
255 j != ctx)
256 found = 1;
257 }
258
259 if (!found) {
260 SET_CONTEXTIDR_ASID(base, ctx, i);
261 break;
262 }
263 }
264 BUG_ON(found);
265 }
266
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700267 /* Enable the MMU */
268 SET_M(base, ctx, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700269 mb();
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700270}
271
Stepan Moskovchenkoff2d3662011-08-31 17:13:32 -0700272static int msm_iommu_domain_init(struct iommu_domain *domain, int flags)
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700273{
274 struct msm_priv *priv = kzalloc(sizeof(*priv), GFP_KERNEL);
275
276 if (!priv)
277 goto fail_nomem;
278
279 INIT_LIST_HEAD(&priv->list_attached);
280 priv->pgtable = (unsigned long *)__get_free_pages(GFP_KERNEL,
281 get_order(SZ_16K));
282
283 if (!priv->pgtable)
284 goto fail_nomem;
285
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700286#ifdef CONFIG_IOMMU_PGTABLES_L2
287 priv->redirect = flags & MSM_IOMMU_DOMAIN_PT_CACHEABLE;
288#endif
289
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700290 memset(priv->pgtable, 0, SZ_16K);
291 domain->priv = priv;
292 return 0;
293
294fail_nomem:
295 kfree(priv);
296 return -ENOMEM;
297}
298
299static void msm_iommu_domain_destroy(struct iommu_domain *domain)
300{
301 struct msm_priv *priv;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700302 unsigned long *fl_table;
303 int i;
304
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800305 mutex_lock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700306 priv = domain->priv;
307 domain->priv = NULL;
308
309 if (priv) {
310 fl_table = priv->pgtable;
311
312 for (i = 0; i < NUM_FL_PTE; i++)
313 if ((fl_table[i] & 0x03) == FL_TYPE_TABLE)
314 free_page((unsigned long) __va(((fl_table[i]) &
315 FL_BASE_MASK)));
316
317 free_pages((unsigned long)priv->pgtable, get_order(SZ_16K));
318 priv->pgtable = NULL;
319 }
320
321 kfree(priv);
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800322 mutex_unlock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700323}
324
325static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
326{
327 struct msm_priv *priv;
328 struct msm_iommu_ctx_dev *ctx_dev;
329 struct msm_iommu_drvdata *iommu_drvdata;
330 struct msm_iommu_ctx_drvdata *ctx_drvdata;
331 struct msm_iommu_ctx_drvdata *tmp_drvdata;
332 int ret = 0;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700333
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800334 mutex_lock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700335
336 priv = domain->priv;
337
338 if (!priv || !dev) {
339 ret = -EINVAL;
340 goto fail;
341 }
342
343 iommu_drvdata = dev_get_drvdata(dev->parent);
344 ctx_drvdata = dev_get_drvdata(dev);
345 ctx_dev = dev->platform_data;
346
347 if (!iommu_drvdata || !ctx_drvdata || !ctx_dev) {
348 ret = -EINVAL;
349 goto fail;
350 }
351
Stepan Moskovchenko00d4b2b2010-11-12 19:29:56 -0800352 if (!list_empty(&ctx_drvdata->attached_elm)) {
353 ret = -EBUSY;
354 goto fail;
355 }
356
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700357 list_for_each_entry(tmp_drvdata, &priv->list_attached, attached_elm)
358 if (tmp_drvdata == ctx_drvdata) {
359 ret = -EBUSY;
360 goto fail;
361 }
362
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800363 ret = __enable_clocks(iommu_drvdata);
364 if (ret)
365 goto fail;
366
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700367 __program_context(iommu_drvdata->base, ctx_dev->num, iommu_drvdata->ncb,
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700368 __pa(priv->pgtable), priv->redirect);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700369
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800370 __disable_clocks(iommu_drvdata);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700371 list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700372
373fail:
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800374 mutex_unlock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700375 return ret;
376}
377
378static void msm_iommu_detach_dev(struct iommu_domain *domain,
379 struct device *dev)
380{
381 struct msm_priv *priv;
382 struct msm_iommu_ctx_dev *ctx_dev;
383 struct msm_iommu_drvdata *iommu_drvdata;
384 struct msm_iommu_ctx_drvdata *ctx_drvdata;
Stepan Moskovchenko33069732010-11-12 19:30:00 -0800385 int ret;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700386
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800387 mutex_lock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700388 priv = domain->priv;
389
390 if (!priv || !dev)
391 goto fail;
392
393 iommu_drvdata = dev_get_drvdata(dev->parent);
394 ctx_drvdata = dev_get_drvdata(dev);
395 ctx_dev = dev->platform_data;
396
397 if (!iommu_drvdata || !ctx_drvdata || !ctx_dev)
398 goto fail;
399
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800400 ret = __enable_clocks(iommu_drvdata);
401 if (ret)
402 goto fail;
403
Stepan Moskovchenkof17c16c2011-08-05 12:16:39 -0700404 SET_TLBIASID(iommu_drvdata->base, ctx_dev->num,
405 GET_CONTEXTIDR_ASID(iommu_drvdata->base, ctx_dev->num));
406
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700407 __reset_context(iommu_drvdata->base, ctx_dev->num);
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800408 __disable_clocks(iommu_drvdata);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700409 list_del_init(&ctx_drvdata->attached_elm);
410
411fail:
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800412 mutex_unlock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700413}
414
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700415static int __get_pgprot(int prot, int len)
416{
417 unsigned int pgprot;
Stepan Moskovchenko6ee3be82011-11-08 15:24:53 -0800418 int tex;
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700419
Stepan Moskovchenko6ee3be82011-11-08 15:24:53 -0800420 if (prot & IOMMU_CACHE)
421 tex = (pgprot_kernel >> 2) & 0x07;
422 else
423 tex = msm_iommu_tex_class[MSM_IOMMU_ATTR_NONCACHED];
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700424
425 if (tex < 0 || tex > NUM_TEX_CLASS - 1)
426 return 0;
427
428 if (len == SZ_16M || len == SZ_1M) {
Stepan Moskovchenko6ee3be82011-11-08 15:24:53 -0800429 pgprot = FL_SHARED;
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700430 pgprot |= tex & 0x01 ? FL_BUFFERABLE : 0;
431 pgprot |= tex & 0x02 ? FL_CACHEABLE : 0;
432 pgprot |= tex & 0x04 ? FL_TEX0 : 0;
433 } else {
Stepan Moskovchenko6ee3be82011-11-08 15:24:53 -0800434 pgprot = SL_SHARED;
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700435 pgprot |= tex & 0x01 ? SL_BUFFERABLE : 0;
436 pgprot |= tex & 0x02 ? SL_CACHEABLE : 0;
437 pgprot |= tex & 0x04 ? SL_TEX0 : 0;
438 }
439
440 return pgprot;
441}
442
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700443static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
444 phys_addr_t pa, int order, int prot)
445{
446 struct msm_priv *priv;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700447 unsigned long *fl_table;
448 unsigned long *fl_pte;
449 unsigned long fl_offset;
450 unsigned long *sl_table;
451 unsigned long *sl_pte;
452 unsigned long sl_offset;
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800453 unsigned int pgprot;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700454 size_t len = 0x1000UL << order;
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700455 int ret = 0;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700456
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800457 mutex_lock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700458
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800459 priv = domain->priv;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700460 if (!priv) {
461 ret = -EINVAL;
462 goto fail;
463 }
464
465 fl_table = priv->pgtable;
466
467 if (len != SZ_16M && len != SZ_1M &&
468 len != SZ_64K && len != SZ_4K) {
469 pr_debug("Bad size: %d\n", len);
470 ret = -EINVAL;
471 goto fail;
472 }
473
474 if (!fl_table) {
475 pr_debug("Null page table\n");
476 ret = -EINVAL;
477 goto fail;
478 }
479
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700480 pgprot = __get_pgprot(prot, len);
481
482 if (!pgprot) {
483 ret = -EINVAL;
484 goto fail;
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800485 }
486
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700487 fl_offset = FL_OFFSET(va); /* Upper 12 bits */
488 fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
489
490 if (len == SZ_16M) {
491 int i = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700492
493 for (i = 0; i < 16; i++)
494 if (*(fl_pte+i)) {
495 ret = -EBUSY;
496 goto fail;
497 }
498
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700499 for (i = 0; i < 16; i++)
500 *(fl_pte+i) = (pa & 0xFF000000) | FL_SUPERSECTION |
501 FL_AP_READ | FL_AP_WRITE | FL_TYPE_SECT |
Stepan Moskovchenko2e8c8ba2011-02-24 18:00:41 -0800502 FL_SHARED | FL_NG | pgprot;
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700503 if (!priv->redirect)
504 clean_pte(fl_pte, fl_pte + 16);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700505 }
506
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700507 if (len == SZ_1M) {
508 if (*fl_pte) {
509 ret = -EBUSY;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700510 goto fail;
511 }
512
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700513 *fl_pte = (pa & 0xFFF00000) | FL_AP_READ | FL_AP_WRITE | FL_NG |
514 FL_TYPE_SECT | FL_SHARED | pgprot;
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700515 if (!priv->redirect)
516 clean_pte(fl_pte, fl_pte + 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700517 }
518
519 /* Need a 2nd level table */
520 if (len == SZ_4K || len == SZ_64K) {
521
522 if (*fl_pte == 0) {
523 unsigned long *sl;
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800524 sl = (unsigned long *) __get_free_pages(GFP_KERNEL,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700525 get_order(SZ_4K));
526
527 if (!sl) {
528 pr_debug("Could not allocate second level table\n");
529 ret = -ENOMEM;
530 goto fail;
531 }
532 memset(sl, 0, SZ_4K);
533
534 *fl_pte = ((((int)__pa(sl)) & FL_BASE_MASK) | \
535 FL_TYPE_TABLE);
Stepan Moskovchenko094475d2011-08-03 13:38:29 -0700536
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700537 if (!priv->redirect)
538 clean_pte(fl_pte, fl_pte + 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700539 }
540
541 if (!(*fl_pte & FL_TYPE_TABLE)) {
542 ret = -EBUSY;
543 goto fail;
544 }
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700545 }
546
547 sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
548 sl_offset = SL_OFFSET(va);
549 sl_pte = sl_table + sl_offset;
550
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700551 if (len == SZ_4K) {
552 if (*sl_pte) {
553 ret = -EBUSY;
554 goto fail;
555 }
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700556
Stepan Moskovchenko2e8c8ba2011-02-24 18:00:41 -0800557 *sl_pte = (pa & SL_BASE_MASK_SMALL) | SL_AP0 | SL_AP1 | SL_NG |
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800558 SL_SHARED | SL_TYPE_SMALL | pgprot;
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700559 if (!priv->redirect)
560 clean_pte(sl_pte, sl_pte + 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700561 }
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700562
563 if (len == SZ_64K) {
564 int i;
565
566 for (i = 0; i < 16; i++)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700567 if (*(sl_pte+i)) {
568 ret = -EBUSY;
569 goto fail;
570 }
571
572 for (i = 0; i < 16; i++)
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700573 *(sl_pte+i) = (pa & SL_BASE_MASK_LARGE) | SL_AP0 |
Stepan Moskovchenko2e8c8ba2011-02-24 18:00:41 -0800574 SL_NG | SL_AP1 | SL_SHARED | SL_TYPE_LARGE | pgprot;
Stepan Moskovchenko094475d2011-08-03 13:38:29 -0700575
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700576 if (!priv->redirect)
577 clean_pte(sl_pte, sl_pte + 16);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700578 }
579
Stepan Moskovchenkobd1ad612011-08-03 16:24:54 -0700580 ret = __flush_iotlb_va(domain, va);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700581fail:
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800582 mutex_unlock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700583 return ret;
584}
585
586static int msm_iommu_unmap(struct iommu_domain *domain, unsigned long va,
587 int order)
588{
589 struct msm_priv *priv;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700590 unsigned long *fl_table;
591 unsigned long *fl_pte;
592 unsigned long fl_offset;
593 unsigned long *sl_table;
594 unsigned long *sl_pte;
595 unsigned long sl_offset;
596 size_t len = 0x1000UL << order;
597 int i, ret = 0;
598
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800599 mutex_lock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700600
601 priv = domain->priv;
602
603 if (!priv) {
604 ret = -ENODEV;
605 goto fail;
606 }
607
608 fl_table = priv->pgtable;
609
610 if (len != SZ_16M && len != SZ_1M &&
611 len != SZ_64K && len != SZ_4K) {
612 pr_debug("Bad length: %d\n", len);
613 ret = -EINVAL;
614 goto fail;
615 }
616
617 if (!fl_table) {
618 pr_debug("Null page table\n");
619 ret = -EINVAL;
620 goto fail;
621 }
622
623 fl_offset = FL_OFFSET(va); /* Upper 12 bits */
624 fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
625
626 if (*fl_pte == 0) {
627 pr_debug("First level PTE is 0\n");
628 ret = -ENODEV;
629 goto fail;
630 }
631
632 /* Unmap supersection */
Stepan Moskovchenko094475d2011-08-03 13:38:29 -0700633 if (len == SZ_16M) {
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700634 for (i = 0; i < 16; i++)
635 *(fl_pte+i) = 0;
636
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700637 if (!priv->redirect)
638 clean_pte(fl_pte, fl_pte + 16);
Stepan Moskovchenko094475d2011-08-03 13:38:29 -0700639 }
640
641 if (len == SZ_1M) {
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700642 *fl_pte = 0;
643
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700644 if (!priv->redirect)
645 clean_pte(fl_pte, fl_pte + 1);
Stepan Moskovchenko094475d2011-08-03 13:38:29 -0700646 }
647
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700648 sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
649 sl_offset = SL_OFFSET(va);
650 sl_pte = sl_table + sl_offset;
651
652 if (len == SZ_64K) {
653 for (i = 0; i < 16; i++)
654 *(sl_pte+i) = 0;
Stepan Moskovchenko094475d2011-08-03 13:38:29 -0700655
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700656 if (!priv->redirect)
657 clean_pte(sl_pte, sl_pte + 16);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700658 }
659
Stepan Moskovchenko094475d2011-08-03 13:38:29 -0700660 if (len == SZ_4K) {
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700661 *sl_pte = 0;
662
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700663 if (!priv->redirect)
664 clean_pte(sl_pte, sl_pte + 1);
Stepan Moskovchenko094475d2011-08-03 13:38:29 -0700665 }
666
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700667 if (len == SZ_4K || len == SZ_64K) {
668 int used = 0;
669
670 for (i = 0; i < NUM_SL_PTE; i++)
671 if (sl_table[i])
672 used = 1;
673 if (!used) {
674 free_page((unsigned long)sl_table);
675 *fl_pte = 0;
Stepan Moskovchenko094475d2011-08-03 13:38:29 -0700676
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700677 if (!priv->redirect)
678 clean_pte(fl_pte, fl_pte + 1);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700679 }
680 }
681
Stepan Moskovchenkobd1ad612011-08-03 16:24:54 -0700682 ret = __flush_iotlb_va(domain, va);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700683fail:
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800684 mutex_unlock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700685 return ret;
686}
687
Jeremy Gebben74e57d42012-03-23 10:26:11 -0600688static unsigned int get_phys_addr(struct scatterlist *sg)
689{
690 /*
691 * Try sg_dma_address first so that we can
692 * map carveout regions that do not have a
693 * struct page associated with them.
694 */
695 unsigned int pa = sg_dma_address(sg);
696 if (pa == 0)
697 pa = sg_phys(sg);
698 return pa;
699}
700
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700701static int msm_iommu_map_range(struct iommu_domain *domain, unsigned int va,
702 struct scatterlist *sg, unsigned int len,
703 int prot)
704{
705 unsigned int pa;
706 unsigned int offset = 0;
707 unsigned int pgprot;
708 unsigned long *fl_table;
709 unsigned long *fl_pte;
710 unsigned long fl_offset;
711 unsigned long *sl_table;
712 unsigned long sl_offset, sl_start;
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700713 unsigned int chunk_offset = 0;
714 unsigned int chunk_pa;
715 int ret = 0;
716 struct msm_priv *priv;
717
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800718 mutex_lock(&msm_iommu_lock);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700719
720 BUG_ON(len & (SZ_4K - 1));
721
722 priv = domain->priv;
723 fl_table = priv->pgtable;
724
725 pgprot = __get_pgprot(prot, SZ_4K);
726
727 if (!pgprot) {
728 ret = -EINVAL;
729 goto fail;
730 }
731
732 fl_offset = FL_OFFSET(va); /* Upper 12 bits */
733 fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
734
735 sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
736 sl_offset = SL_OFFSET(va);
737
Jeremy Gebben74e57d42012-03-23 10:26:11 -0600738 chunk_pa = get_phys_addr(sg);
739 if (chunk_pa == 0) {
740 pr_debug("No dma address for sg %p\n", sg);
741 ret = -EINVAL;
742 goto fail;
743 }
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700744
745 while (offset < len) {
746 /* Set up a 2nd level page table if one doesn't exist */
747 if (*fl_pte == 0) {
748 sl_table = (unsigned long *)
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800749 __get_free_pages(GFP_KERNEL, get_order(SZ_4K));
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700750
751 if (!sl_table) {
752 pr_debug("Could not allocate second level table\n");
753 ret = -ENOMEM;
754 goto fail;
755 }
756
757 memset(sl_table, 0, SZ_4K);
758 *fl_pte = ((((int)__pa(sl_table)) & FL_BASE_MASK) |
759 FL_TYPE_TABLE);
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700760 if (!priv->redirect)
761 clean_pte(fl_pte, fl_pte + 1);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700762 } else
763 sl_table = (unsigned long *)
764 __va(((*fl_pte) & FL_BASE_MASK));
765
766 /* Keep track of initial position so we
767 * don't clean more than we have to
768 */
769 sl_start = sl_offset;
770
771 /* Build the 2nd level page table */
772 while (offset < len && sl_offset < NUM_SL_PTE) {
773 pa = chunk_pa + chunk_offset;
774 sl_table[sl_offset] = (pa & SL_BASE_MASK_SMALL) |
775 pgprot | SL_AP0 | SL_AP1 | SL_NG |
776 SL_SHARED | SL_TYPE_SMALL;
777 sl_offset++;
778 offset += SZ_4K;
779
780 chunk_offset += SZ_4K;
781
782 if (chunk_offset >= sg->length && offset < len) {
783 chunk_offset = 0;
784 sg = sg_next(sg);
Jeremy Gebben74e57d42012-03-23 10:26:11 -0600785 chunk_pa = get_phys_addr(sg);
786 if (chunk_pa == 0) {
787 pr_debug("No dma address for sg %p\n",
788 sg);
789 ret = -EINVAL;
790 goto fail;
791 }
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700792 }
793 }
794
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700795 if (!priv->redirect)
796 clean_pte(sl_table + sl_start, sl_table + sl_offset);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700797
798 fl_pte++;
799 sl_offset = 0;
800 }
801 __flush_iotlb(domain);
802fail:
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800803 mutex_unlock(&msm_iommu_lock);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700804 return ret;
805}
806
807
808static int msm_iommu_unmap_range(struct iommu_domain *domain, unsigned int va,
809 unsigned int len)
810{
811 unsigned int offset = 0;
812 unsigned long *fl_table;
813 unsigned long *fl_pte;
814 unsigned long fl_offset;
815 unsigned long *sl_table;
816 unsigned long sl_start, sl_end;
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700817 int used, i;
818 struct msm_priv *priv;
819
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800820 mutex_lock(&msm_iommu_lock);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700821
822 BUG_ON(len & (SZ_4K - 1));
823
824 priv = domain->priv;
825 fl_table = priv->pgtable;
826
827 fl_offset = FL_OFFSET(va); /* Upper 12 bits */
828 fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
829
830 sl_start = SL_OFFSET(va);
831
832 while (offset < len) {
833 sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
834 sl_end = ((len - offset) / SZ_4K) + sl_start;
835
836 if (sl_end > NUM_SL_PTE)
837 sl_end = NUM_SL_PTE;
838
839 memset(sl_table + sl_start, 0, (sl_end - sl_start) * 4);
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700840 if (!priv->redirect)
841 clean_pte(sl_table + sl_start, sl_table + sl_end);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700842
843 offset += (sl_end - sl_start) * SZ_4K;
844
845 /* Unmap and free the 2nd level table if all mappings in it
846 * were removed. This saves memory, but the table will need
847 * to be re-allocated the next time someone tries to map these
848 * VAs.
849 */
850 used = 0;
851
852 /* If we just unmapped the whole table, don't bother
853 * seeing if there are still used entries left.
854 */
855 if (sl_end - sl_start != NUM_SL_PTE)
856 for (i = 0; i < NUM_SL_PTE; i++)
857 if (sl_table[i]) {
858 used = 1;
859 break;
860 }
861 if (!used) {
862 free_page((unsigned long)sl_table);
863 *fl_pte = 0;
864
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700865 if (!priv->redirect)
866 clean_pte(fl_pte, fl_pte + 1);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700867 }
868
869 sl_start = 0;
870 fl_pte++;
871 }
872
873 __flush_iotlb(domain);
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800874 mutex_unlock(&msm_iommu_lock);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700875 return 0;
876}
877
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700878static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain,
879 unsigned long va)
880{
881 struct msm_priv *priv;
882 struct msm_iommu_drvdata *iommu_drvdata;
883 struct msm_iommu_ctx_drvdata *ctx_drvdata;
884 unsigned int par;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700885 void __iomem *base;
886 phys_addr_t ret = 0;
887 int ctx;
888
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800889 mutex_lock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700890
891 priv = domain->priv;
892 if (list_empty(&priv->list_attached))
893 goto fail;
894
895 ctx_drvdata = list_entry(priv->list_attached.next,
896 struct msm_iommu_ctx_drvdata, attached_elm);
897 iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
898
899 base = iommu_drvdata->base;
900 ctx = ctx_drvdata->num;
901
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800902 ret = __enable_clocks(iommu_drvdata);
903 if (ret)
904 goto fail;
905
Stepan Moskovchenkob0e78082011-02-28 16:04:55 -0800906 SET_V2PPR(base, ctx, va & V2Pxx_VA);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700907
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700908 mb();
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700909 par = GET_PAR(base, ctx);
910
911 /* We are dealing with a supersection */
912 if (GET_NOFAULT_SS(base, ctx))
913 ret = (par & 0xFF000000) | (va & 0x00FFFFFF);
914 else /* Upper 20 bits from PAR, lower 12 from VA */
915 ret = (par & 0xFFFFF000) | (va & 0x00000FFF);
916
Stepan Moskovchenko33069732010-11-12 19:30:00 -0800917 if (GET_FAULT(base, ctx))
918 ret = 0;
919
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800920 __disable_clocks(iommu_drvdata);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700921fail:
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800922 mutex_unlock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700923 return ret;
924}
925
926static int msm_iommu_domain_has_cap(struct iommu_domain *domain,
927 unsigned long cap)
928{
929 return 0;
930}
931
932static void print_ctx_regs(void __iomem *base, int ctx)
933{
934 unsigned int fsr = GET_FSR(base, ctx);
935 pr_err("FAR = %08x PAR = %08x\n",
936 GET_FAR(base, ctx), GET_PAR(base, ctx));
937 pr_err("FSR = %08x [%s%s%s%s%s%s%s%s%s%s]\n", fsr,
938 (fsr & 0x02) ? "TF " : "",
939 (fsr & 0x04) ? "AFF " : "",
940 (fsr & 0x08) ? "APF " : "",
941 (fsr & 0x10) ? "TLBMF " : "",
942 (fsr & 0x20) ? "HTWDEEF " : "",
943 (fsr & 0x40) ? "HTWSEEF " : "",
944 (fsr & 0x80) ? "MHF " : "",
945 (fsr & 0x10000) ? "SL " : "",
946 (fsr & 0x40000000) ? "SS " : "",
947 (fsr & 0x80000000) ? "MULTI " : "");
948
949 pr_err("FSYNR0 = %08x FSYNR1 = %08x\n",
950 GET_FSYNR0(base, ctx), GET_FSYNR1(base, ctx));
951 pr_err("TTBR0 = %08x TTBR1 = %08x\n",
952 GET_TTBR0(base, ctx), GET_TTBR1(base, ctx));
953 pr_err("SCTLR = %08x ACTLR = %08x\n",
954 GET_SCTLR(base, ctx), GET_ACTLR(base, ctx));
955 pr_err("PRRR = %08x NMRR = %08x\n",
956 GET_PRRR(base, ctx), GET_NMRR(base, ctx));
957}
958
959irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id)
960{
961 struct msm_iommu_drvdata *drvdata = dev_id;
962 void __iomem *base;
Stepan Moskovchenko33069732010-11-12 19:30:00 -0800963 unsigned int fsr;
Stepan Moskovchenkoa43d8c12011-02-24 18:00:42 -0800964 int i, ret;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700965
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800966 mutex_lock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700967
968 if (!drvdata) {
969 pr_err("Invalid device ID in context interrupt handler\n");
970 goto fail;
971 }
972
973 base = drvdata->base;
974
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700975 pr_err("Unexpected IOMMU page fault!\n");
976 pr_err("base = %08x\n", (unsigned int) base);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700977 pr_err("name = %s\n", drvdata->name);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700978
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800979 ret = __enable_clocks(drvdata);
980 if (ret)
981 goto fail;
982
Stepan Moskovchenkoa43d8c12011-02-24 18:00:42 -0800983 for (i = 0; i < drvdata->ncb; i++) {
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700984 fsr = GET_FSR(base, i);
985 if (fsr) {
986 pr_err("Fault occurred in context %d.\n", i);
987 pr_err("Interesting registers:\n");
988 print_ctx_regs(base, i);
989 SET_FSR(base, i, 0x4000000F);
990 }
991 }
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800992 __disable_clocks(drvdata);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700993fail:
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800994 mutex_unlock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700995 return 0;
996}
997
Shubhraprakash Das4c436f22011-12-02 18:01:57 -0700998static phys_addr_t msm_iommu_get_pt_base_addr(struct iommu_domain *domain)
999{
1000 struct msm_priv *priv = domain->priv;
1001 return __pa(priv->pgtable);
1002}
1003
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -07001004static struct iommu_ops msm_iommu_ops = {
1005 .domain_init = msm_iommu_domain_init,
1006 .domain_destroy = msm_iommu_domain_destroy,
1007 .attach_dev = msm_iommu_attach_dev,
1008 .detach_dev = msm_iommu_detach_dev,
1009 .map = msm_iommu_map,
1010 .unmap = msm_iommu_unmap,
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -07001011 .map_range = msm_iommu_map_range,
1012 .unmap_range = msm_iommu_unmap_range,
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -07001013 .iova_to_phys = msm_iommu_iova_to_phys,
Shubhraprakash Das4c436f22011-12-02 18:01:57 -07001014 .domain_has_cap = msm_iommu_domain_has_cap,
1015 .get_pt_base_addr = msm_iommu_get_pt_base_addr
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -07001016};
1017
Stepan Moskovchenko100832c2010-11-15 18:20:08 -08001018static int __init get_tex_class(int icp, int ocp, int mt, int nos)
1019{
1020 int i = 0;
1021 unsigned int prrr = 0;
1022 unsigned int nmrr = 0;
1023 int c_icp, c_ocp, c_mt, c_nos;
1024
1025 RCP15_PRRR(prrr);
1026 RCP15_NMRR(nmrr);
1027
1028 for (i = 0; i < NUM_TEX_CLASS; i++) {
1029 c_nos = PRRR_NOS(prrr, i);
1030 c_mt = PRRR_MT(prrr, i);
1031 c_icp = NMRR_ICP(nmrr, i);
1032 c_ocp = NMRR_OCP(nmrr, i);
1033
1034 if (icp == c_icp && ocp == c_ocp && c_mt == mt && c_nos == nos)
1035 return i;
1036 }
1037
1038 return -ENODEV;
1039}
1040
1041static void __init setup_iommu_tex_classes(void)
1042{
1043 msm_iommu_tex_class[MSM_IOMMU_ATTR_NONCACHED] =
1044 get_tex_class(CP_NONCACHED, CP_NONCACHED, MT_NORMAL, 1);
1045
1046 msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WB_WA] =
1047 get_tex_class(CP_WB_WA, CP_WB_WA, MT_NORMAL, 1);
1048
1049 msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WB_NWA] =
1050 get_tex_class(CP_WB_NWA, CP_WB_NWA, MT_NORMAL, 1);
1051
1052 msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WT] =
1053 get_tex_class(CP_WT, CP_WT, MT_NORMAL, 1);
1054}
1055
Stepan Moskovchenko516cbc72010-11-12 19:29:53 -08001056static int __init msm_iommu_init(void)
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -07001057{
Stepan Moskovchenko15f209c2011-10-31 15:32:44 -07001058 if (!msm_soc_version_supports_iommu())
1059 return -ENODEV;
1060
Stepan Moskovchenko100832c2010-11-15 18:20:08 -08001061 setup_iommu_tex_classes();
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -07001062 register_iommu(&msm_iommu_ops);
1063 return 0;
1064}
1065
1066subsys_initcall(msm_iommu_init);
1067
1068MODULE_LICENSE("GPL v2");
1069MODULE_AUTHOR("Stepan Moskovchenko <stepanm@codeaurora.org>");