blob: 853888a6223c6d067a675acccce05c5472f8a2df [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
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700688static int msm_iommu_map_range(struct iommu_domain *domain, unsigned int va,
689 struct scatterlist *sg, unsigned int len,
690 int prot)
691{
692 unsigned int pa;
693 unsigned int offset = 0;
694 unsigned int pgprot;
695 unsigned long *fl_table;
696 unsigned long *fl_pte;
697 unsigned long fl_offset;
698 unsigned long *sl_table;
699 unsigned long sl_offset, sl_start;
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700700 unsigned int chunk_offset = 0;
701 unsigned int chunk_pa;
702 int ret = 0;
703 struct msm_priv *priv;
704
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800705 mutex_lock(&msm_iommu_lock);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700706
707 BUG_ON(len & (SZ_4K - 1));
708
709 priv = domain->priv;
710 fl_table = priv->pgtable;
711
712 pgprot = __get_pgprot(prot, SZ_4K);
713
714 if (!pgprot) {
715 ret = -EINVAL;
716 goto fail;
717 }
718
719 fl_offset = FL_OFFSET(va); /* Upper 12 bits */
720 fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
721
722 sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
723 sl_offset = SL_OFFSET(va);
724
725 chunk_pa = sg_phys(sg);
726
727 while (offset < len) {
728 /* Set up a 2nd level page table if one doesn't exist */
729 if (*fl_pte == 0) {
730 sl_table = (unsigned long *)
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800731 __get_free_pages(GFP_KERNEL, get_order(SZ_4K));
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700732
733 if (!sl_table) {
734 pr_debug("Could not allocate second level table\n");
735 ret = -ENOMEM;
736 goto fail;
737 }
738
739 memset(sl_table, 0, SZ_4K);
740 *fl_pte = ((((int)__pa(sl_table)) & FL_BASE_MASK) |
741 FL_TYPE_TABLE);
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700742 if (!priv->redirect)
743 clean_pte(fl_pte, fl_pte + 1);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700744 } else
745 sl_table = (unsigned long *)
746 __va(((*fl_pte) & FL_BASE_MASK));
747
748 /* Keep track of initial position so we
749 * don't clean more than we have to
750 */
751 sl_start = sl_offset;
752
753 /* Build the 2nd level page table */
754 while (offset < len && sl_offset < NUM_SL_PTE) {
755 pa = chunk_pa + chunk_offset;
756 sl_table[sl_offset] = (pa & SL_BASE_MASK_SMALL) |
757 pgprot | SL_AP0 | SL_AP1 | SL_NG |
758 SL_SHARED | SL_TYPE_SMALL;
759 sl_offset++;
760 offset += SZ_4K;
761
762 chunk_offset += SZ_4K;
763
764 if (chunk_offset >= sg->length && offset < len) {
765 chunk_offset = 0;
766 sg = sg_next(sg);
767 chunk_pa = sg_phys(sg);
768 }
769 }
770
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700771 if (!priv->redirect)
772 clean_pte(sl_table + sl_start, sl_table + sl_offset);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700773
774 fl_pte++;
775 sl_offset = 0;
776 }
777 __flush_iotlb(domain);
778fail:
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800779 mutex_unlock(&msm_iommu_lock);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700780 return ret;
781}
782
783
784static int msm_iommu_unmap_range(struct iommu_domain *domain, unsigned int va,
785 unsigned int len)
786{
787 unsigned int offset = 0;
788 unsigned long *fl_table;
789 unsigned long *fl_pte;
790 unsigned long fl_offset;
791 unsigned long *sl_table;
792 unsigned long sl_start, sl_end;
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700793 int used, i;
794 struct msm_priv *priv;
795
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800796 mutex_lock(&msm_iommu_lock);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700797
798 BUG_ON(len & (SZ_4K - 1));
799
800 priv = domain->priv;
801 fl_table = priv->pgtable;
802
803 fl_offset = FL_OFFSET(va); /* Upper 12 bits */
804 fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
805
806 sl_start = SL_OFFSET(va);
807
808 while (offset < len) {
809 sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
810 sl_end = ((len - offset) / SZ_4K) + sl_start;
811
812 if (sl_end > NUM_SL_PTE)
813 sl_end = NUM_SL_PTE;
814
815 memset(sl_table + sl_start, 0, (sl_end - sl_start) * 4);
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700816 if (!priv->redirect)
817 clean_pte(sl_table + sl_start, sl_table + sl_end);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700818
819 offset += (sl_end - sl_start) * SZ_4K;
820
821 /* Unmap and free the 2nd level table if all mappings in it
822 * were removed. This saves memory, but the table will need
823 * to be re-allocated the next time someone tries to map these
824 * VAs.
825 */
826 used = 0;
827
828 /* If we just unmapped the whole table, don't bother
829 * seeing if there are still used entries left.
830 */
831 if (sl_end - sl_start != NUM_SL_PTE)
832 for (i = 0; i < NUM_SL_PTE; i++)
833 if (sl_table[i]) {
834 used = 1;
835 break;
836 }
837 if (!used) {
838 free_page((unsigned long)sl_table);
839 *fl_pte = 0;
840
Stepan Moskovchenkob2438892011-08-31 17:16:19 -0700841 if (!priv->redirect)
842 clean_pte(fl_pte, fl_pte + 1);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700843 }
844
845 sl_start = 0;
846 fl_pte++;
847 }
848
849 __flush_iotlb(domain);
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800850 mutex_unlock(&msm_iommu_lock);
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700851 return 0;
852}
853
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700854static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain,
855 unsigned long va)
856{
857 struct msm_priv *priv;
858 struct msm_iommu_drvdata *iommu_drvdata;
859 struct msm_iommu_ctx_drvdata *ctx_drvdata;
860 unsigned int par;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700861 void __iomem *base;
862 phys_addr_t ret = 0;
863 int ctx;
864
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800865 mutex_lock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700866
867 priv = domain->priv;
868 if (list_empty(&priv->list_attached))
869 goto fail;
870
871 ctx_drvdata = list_entry(priv->list_attached.next,
872 struct msm_iommu_ctx_drvdata, attached_elm);
873 iommu_drvdata = dev_get_drvdata(ctx_drvdata->pdev->dev.parent);
874
875 base = iommu_drvdata->base;
876 ctx = ctx_drvdata->num;
877
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800878 ret = __enable_clocks(iommu_drvdata);
879 if (ret)
880 goto fail;
881
Stepan Moskovchenkob0e78082011-02-28 16:04:55 -0800882 SET_V2PPR(base, ctx, va & V2Pxx_VA);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700883
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700884 mb();
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700885 par = GET_PAR(base, ctx);
886
887 /* We are dealing with a supersection */
888 if (GET_NOFAULT_SS(base, ctx))
889 ret = (par & 0xFF000000) | (va & 0x00FFFFFF);
890 else /* Upper 20 bits from PAR, lower 12 from VA */
891 ret = (par & 0xFFFFF000) | (va & 0x00000FFF);
892
Stepan Moskovchenko33069732010-11-12 19:30:00 -0800893 if (GET_FAULT(base, ctx))
894 ret = 0;
895
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800896 __disable_clocks(iommu_drvdata);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700897fail:
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800898 mutex_unlock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700899 return ret;
900}
901
902static int msm_iommu_domain_has_cap(struct iommu_domain *domain,
903 unsigned long cap)
904{
905 return 0;
906}
907
908static void print_ctx_regs(void __iomem *base, int ctx)
909{
910 unsigned int fsr = GET_FSR(base, ctx);
911 pr_err("FAR = %08x PAR = %08x\n",
912 GET_FAR(base, ctx), GET_PAR(base, ctx));
913 pr_err("FSR = %08x [%s%s%s%s%s%s%s%s%s%s]\n", fsr,
914 (fsr & 0x02) ? "TF " : "",
915 (fsr & 0x04) ? "AFF " : "",
916 (fsr & 0x08) ? "APF " : "",
917 (fsr & 0x10) ? "TLBMF " : "",
918 (fsr & 0x20) ? "HTWDEEF " : "",
919 (fsr & 0x40) ? "HTWSEEF " : "",
920 (fsr & 0x80) ? "MHF " : "",
921 (fsr & 0x10000) ? "SL " : "",
922 (fsr & 0x40000000) ? "SS " : "",
923 (fsr & 0x80000000) ? "MULTI " : "");
924
925 pr_err("FSYNR0 = %08x FSYNR1 = %08x\n",
926 GET_FSYNR0(base, ctx), GET_FSYNR1(base, ctx));
927 pr_err("TTBR0 = %08x TTBR1 = %08x\n",
928 GET_TTBR0(base, ctx), GET_TTBR1(base, ctx));
929 pr_err("SCTLR = %08x ACTLR = %08x\n",
930 GET_SCTLR(base, ctx), GET_ACTLR(base, ctx));
931 pr_err("PRRR = %08x NMRR = %08x\n",
932 GET_PRRR(base, ctx), GET_NMRR(base, ctx));
933}
934
935irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id)
936{
937 struct msm_iommu_drvdata *drvdata = dev_id;
938 void __iomem *base;
Stepan Moskovchenko33069732010-11-12 19:30:00 -0800939 unsigned int fsr;
Stepan Moskovchenkoa43d8c12011-02-24 18:00:42 -0800940 int i, ret;
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700941
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800942 mutex_lock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700943
944 if (!drvdata) {
945 pr_err("Invalid device ID in context interrupt handler\n");
946 goto fail;
947 }
948
949 base = drvdata->base;
950
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700951 pr_err("Unexpected IOMMU page fault!\n");
952 pr_err("base = %08x\n", (unsigned int) base);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700953 pr_err("name = %s\n", drvdata->name);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700954
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800955 ret = __enable_clocks(drvdata);
956 if (ret)
957 goto fail;
958
Stepan Moskovchenkoa43d8c12011-02-24 18:00:42 -0800959 for (i = 0; i < drvdata->ncb; i++) {
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700960 fsr = GET_FSR(base, i);
961 if (fsr) {
962 pr_err("Fault occurred in context %d.\n", i);
963 pr_err("Interesting registers:\n");
964 print_ctx_regs(base, i);
965 SET_FSR(base, i, 0x4000000F);
966 }
967 }
Stepan Moskovchenko41f3f512011-02-24 18:00:39 -0800968 __disable_clocks(drvdata);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700969fail:
Stepan Moskovchenkoc5888f22012-02-14 15:42:05 -0800970 mutex_unlock(&msm_iommu_lock);
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700971 return 0;
972}
973
Shubhraprakash Das4c436f22011-12-02 18:01:57 -0700974static phys_addr_t msm_iommu_get_pt_base_addr(struct iommu_domain *domain)
975{
976 struct msm_priv *priv = domain->priv;
977 return __pa(priv->pgtable);
978}
979
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700980static struct iommu_ops msm_iommu_ops = {
981 .domain_init = msm_iommu_domain_init,
982 .domain_destroy = msm_iommu_domain_destroy,
983 .attach_dev = msm_iommu_attach_dev,
984 .detach_dev = msm_iommu_detach_dev,
985 .map = msm_iommu_map,
986 .unmap = msm_iommu_unmap,
Stepan Moskovchenko04255ee2011-08-11 19:45:23 -0700987 .map_range = msm_iommu_map_range,
988 .unmap_range = msm_iommu_unmap_range,
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700989 .iova_to_phys = msm_iommu_iova_to_phys,
Shubhraprakash Das4c436f22011-12-02 18:01:57 -0700990 .domain_has_cap = msm_iommu_domain_has_cap,
991 .get_pt_base_addr = msm_iommu_get_pt_base_addr
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -0700992};
993
Stepan Moskovchenko100832c2010-11-15 18:20:08 -0800994static int __init get_tex_class(int icp, int ocp, int mt, int nos)
995{
996 int i = 0;
997 unsigned int prrr = 0;
998 unsigned int nmrr = 0;
999 int c_icp, c_ocp, c_mt, c_nos;
1000
1001 RCP15_PRRR(prrr);
1002 RCP15_NMRR(nmrr);
1003
1004 for (i = 0; i < NUM_TEX_CLASS; i++) {
1005 c_nos = PRRR_NOS(prrr, i);
1006 c_mt = PRRR_MT(prrr, i);
1007 c_icp = NMRR_ICP(nmrr, i);
1008 c_ocp = NMRR_OCP(nmrr, i);
1009
1010 if (icp == c_icp && ocp == c_ocp && c_mt == mt && c_nos == nos)
1011 return i;
1012 }
1013
1014 return -ENODEV;
1015}
1016
1017static void __init setup_iommu_tex_classes(void)
1018{
1019 msm_iommu_tex_class[MSM_IOMMU_ATTR_NONCACHED] =
1020 get_tex_class(CP_NONCACHED, CP_NONCACHED, MT_NORMAL, 1);
1021
1022 msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WB_WA] =
1023 get_tex_class(CP_WB_WA, CP_WB_WA, MT_NORMAL, 1);
1024
1025 msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WB_NWA] =
1026 get_tex_class(CP_WB_NWA, CP_WB_NWA, MT_NORMAL, 1);
1027
1028 msm_iommu_tex_class[MSM_IOMMU_ATTR_CACHED_WT] =
1029 get_tex_class(CP_WT, CP_WT, MT_NORMAL, 1);
1030}
1031
Stepan Moskovchenko516cbc72010-11-12 19:29:53 -08001032static int __init msm_iommu_init(void)
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -07001033{
Stepan Moskovchenko15f209c2011-10-31 15:32:44 -07001034 if (!msm_soc_version_supports_iommu())
1035 return -ENODEV;
1036
Stepan Moskovchenko100832c2010-11-15 18:20:08 -08001037 setup_iommu_tex_classes();
Stepan Moskovchenko0720d1f2010-08-24 18:31:10 -07001038 register_iommu(&msm_iommu_ops);
1039 return 0;
1040}
1041
1042subsys_initcall(msm_iommu_init);
1043
1044MODULE_LICENSE("GPL v2");
1045MODULE_AUTHOR("Stepan Moskovchenko <stepanm@codeaurora.org>");