Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
msm_iommu_dev.c
Go to the documentation of this file.
1 /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2  *
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 
20 #include <linux/kernel.h>
21 #include <linux/module.h>
22 #include <linux/platform_device.h>
23 #include <linux/io.h>
24 #include <linux/clk.h>
25 #include <linux/iommu.h>
26 #include <linux/interrupt.h>
27 #include <linux/err.h>
28 #include <linux/slab.h>
29 
30 #include <mach/iommu_hw-8xxx.h>
31 #include <mach/iommu.h>
32 #include <mach/clk.h>
33 
35  /* input */
36  const char *name;
37 
38  /* output */
39  struct device *dev;
40 };
41 
42 static struct platform_device *msm_iommu_root_dev;
43 
44 static int each_iommu_ctx(struct device *dev, void *data)
45 {
46  struct iommu_ctx_iter_data *res = data;
47  struct msm_iommu_ctx_dev *c = dev->platform_data;
48 
49  if (!res || !c || !c->name || !res->name)
50  return -EINVAL;
51 
52  if (!strcmp(res->name, c->name)) {
53  res->dev = dev;
54  return 1;
55  }
56  return 0;
57 }
58 
59 static int each_iommu(struct device *dev, void *data)
60 {
61  return device_for_each_child(dev, data, each_iommu_ctx);
62 }
63 
64 struct device *msm_iommu_get_ctx(const char *ctx_name)
65 {
66  struct iommu_ctx_iter_data r;
67  int found;
68 
69  if (!msm_iommu_root_dev) {
70  pr_err("No root IOMMU device.\n");
71  goto fail;
72  }
73 
74  r.name = ctx_name;
75  found = device_for_each_child(&msm_iommu_root_dev->dev, &r, each_iommu);
76 
77  if (!found) {
78  pr_err("Could not find context <%s>\n", ctx_name);
79  goto fail;
80  }
81 
82  return r.dev;
83 fail:
84  return NULL;
85 }
87 
88 static void msm_iommu_reset(void __iomem *base, int ncb)
89 {
90  int ctx;
91 
92  SET_RPUE(base, 0);
93  SET_RPUEIE(base, 0);
94  SET_ESRRESTORE(base, 0);
95  SET_TBE(base, 0);
96  SET_CR(base, 0);
97  SET_SPDMBE(base, 0);
98  SET_TESTBUSCR(base, 0);
99  SET_TLBRSW(base, 0);
100  SET_GLOBAL_TLBIALL(base, 0);
101  SET_RPU_ACR(base, 0);
102  SET_TLBLKCRWE(base, 1);
103 
104  for (ctx = 0; ctx < ncb; ctx++) {
105  SET_BPRCOSH(base, ctx, 0);
106  SET_BPRCISH(base, ctx, 0);
107  SET_BPRCNSH(base, ctx, 0);
108  SET_BPSHCFG(base, ctx, 0);
109  SET_BPMTCFG(base, ctx, 0);
110  SET_ACTLR(base, ctx, 0);
111  SET_SCTLR(base, ctx, 0);
112  SET_FSRRESTORE(base, ctx, 0);
113  SET_TTBR0(base, ctx, 0);
114  SET_TTBR1(base, ctx, 0);
115  SET_TTBCR(base, ctx, 0);
116  SET_BFBCR(base, ctx, 0);
117  SET_PAR(base, ctx, 0);
118  SET_FAR(base, ctx, 0);
119  SET_CTX_TLBIALL(base, ctx, 0);
120  SET_TLBFLPTER(base, ctx, 0);
121  SET_TLBSLPTER(base, ctx, 0);
122  SET_TLBLKCR(base, ctx, 0);
123  SET_PRRR(base, ctx, 0);
124  SET_NMRR(base, ctx, 0);
125  SET_CONTEXTIDR(base, ctx, 0);
126  }
127 }
128 
129 static int msm_iommu_probe(struct platform_device *pdev)
130 {
131  struct resource *r, *r2;
132  struct clk *iommu_clk;
133  struct clk *iommu_pclk;
134  struct msm_iommu_drvdata *drvdata;
135  struct msm_iommu_dev *iommu_dev = pdev->dev.platform_data;
136  void __iomem *regs_base;
138  int ret, irq, par;
139 
140  if (pdev->id == -1) {
141  msm_iommu_root_dev = pdev;
142  return 0;
143  }
144 
145  drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
146 
147  if (!drvdata) {
148  ret = -ENOMEM;
149  goto fail;
150  }
151 
152  if (!iommu_dev) {
153  ret = -ENODEV;
154  goto fail;
155  }
156 
157  iommu_pclk = clk_get(NULL, "smmu_pclk");
158  if (IS_ERR(iommu_pclk)) {
159  ret = -ENODEV;
160  goto fail;
161  }
162 
163  ret = clk_enable(iommu_pclk);
164  if (ret)
165  goto fail_enable;
166 
167  iommu_clk = clk_get(&pdev->dev, "iommu_clk");
168 
169  if (!IS_ERR(iommu_clk)) {
170  if (clk_get_rate(iommu_clk) == 0)
171  clk_set_min_rate(iommu_clk, 1);
172 
173  ret = clk_enable(iommu_clk);
174  if (ret) {
175  clk_put(iommu_clk);
176  goto fail_pclk;
177  }
178  } else
179  iommu_clk = NULL;
180 
181  r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "physbase");
182 
183  if (!r) {
184  ret = -ENODEV;
185  goto fail_clk;
186  }
187 
188  len = resource_size(r);
189 
190  r2 = request_mem_region(r->start, len, r->name);
191  if (!r2) {
192  pr_err("Could not request memory region: start=%p, len=%d\n",
193  (void *) r->start, len);
194  ret = -EBUSY;
195  goto fail_clk;
196  }
197 
198  regs_base = ioremap(r2->start, len);
199 
200  if (!regs_base) {
201  pr_err("Could not ioremap: start=%p, len=%d\n",
202  (void *) r2->start, len);
203  ret = -EBUSY;
204  goto fail_mem;
205  }
206 
207  irq = platform_get_irq_byname(pdev, "secure_irq");
208  if (irq < 0) {
209  ret = -ENODEV;
210  goto fail_io;
211  }
212 
213  msm_iommu_reset(regs_base, iommu_dev->ncb);
214 
215  SET_M(regs_base, 0, 1);
216  SET_PAR(regs_base, 0, 0);
217  SET_V2PCFG(regs_base, 0, 1);
218  SET_V2PPR(regs_base, 0, 0);
219  par = GET_PAR(regs_base, 0);
220  SET_V2PCFG(regs_base, 0, 0);
221  SET_M(regs_base, 0, 0);
222 
223  if (!par) {
224  pr_err("%s: Invalid PAR value detected\n", iommu_dev->name);
225  ret = -ENODEV;
226  goto fail_io;
227  }
228 
229  ret = request_irq(irq, msm_iommu_fault_handler, 0,
230  "msm_iommu_secure_irpt_handler", drvdata);
231  if (ret) {
232  pr_err("Request IRQ %d failed with ret=%d\n", irq, ret);
233  goto fail_io;
234  }
235 
236 
237  drvdata->pclk = iommu_pclk;
238  drvdata->clk = iommu_clk;
239  drvdata->base = regs_base;
240  drvdata->irq = irq;
241  drvdata->ncb = iommu_dev->ncb;
242 
243  pr_info("device %s mapped at %p, irq %d with %d ctx banks\n",
244  iommu_dev->name, regs_base, irq, iommu_dev->ncb);
245 
246  platform_set_drvdata(pdev, drvdata);
247 
248  if (iommu_clk)
249  clk_disable(iommu_clk);
250 
251  clk_disable(iommu_pclk);
252 
253  return 0;
254 fail_io:
255  iounmap(regs_base);
256 fail_mem:
257  release_mem_region(r->start, len);
258 fail_clk:
259  if (iommu_clk) {
260  clk_disable(iommu_clk);
261  clk_put(iommu_clk);
262  }
263 fail_pclk:
264  clk_disable(iommu_pclk);
265 fail_enable:
266  clk_put(iommu_pclk);
267 fail:
268  kfree(drvdata);
269  return ret;
270 }
271 
272 static int msm_iommu_remove(struct platform_device *pdev)
273 {
274  struct msm_iommu_drvdata *drv = NULL;
275 
276  drv = platform_get_drvdata(pdev);
277  if (drv) {
278  if (drv->clk)
279  clk_put(drv->clk);
280  clk_put(drv->pclk);
281  memset(drv, 0, sizeof(*drv));
282  kfree(drv);
283  platform_set_drvdata(pdev, NULL);
284  }
285  return 0;
286 }
287 
288 static int msm_iommu_ctx_probe(struct platform_device *pdev)
289 {
290  struct msm_iommu_ctx_dev *c = pdev->dev.platform_data;
291  struct msm_iommu_drvdata *drvdata;
292  struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL;
293  int i, ret;
294  if (!c || !pdev->dev.parent) {
295  ret = -EINVAL;
296  goto fail;
297  }
298 
299  drvdata = dev_get_drvdata(pdev->dev.parent);
300 
301  if (!drvdata) {
302  ret = -ENODEV;
303  goto fail;
304  }
305 
306  ctx_drvdata = kzalloc(sizeof(*ctx_drvdata), GFP_KERNEL);
307  if (!ctx_drvdata) {
308  ret = -ENOMEM;
309  goto fail;
310  }
311  ctx_drvdata->num = c->num;
312  ctx_drvdata->pdev = pdev;
313 
314  INIT_LIST_HEAD(&ctx_drvdata->attached_elm);
315  platform_set_drvdata(pdev, ctx_drvdata);
316 
317  ret = clk_enable(drvdata->pclk);
318  if (ret)
319  goto fail;
320 
321  if (drvdata->clk) {
322  ret = clk_enable(drvdata->clk);
323  if (ret) {
324  clk_disable(drvdata->pclk);
325  goto fail;
326  }
327  }
328 
329  /* Program the M2V tables for this context */
330  for (i = 0; i < MAX_NUM_MIDS; i++) {
331  int mid = c->mids[i];
332  if (mid == -1)
333  break;
334 
335  SET_M2VCBR_N(drvdata->base, mid, 0);
336  SET_CBACR_N(drvdata->base, c->num, 0);
337 
338  /* Set VMID = 0 */
339  SET_VMID(drvdata->base, mid, 0);
340 
341  /* Set the context number for that MID to this context */
342  SET_CBNDX(drvdata->base, mid, c->num);
343 
344  /* Set MID associated with this context bank to 0*/
345  SET_CBVMID(drvdata->base, c->num, 0);
346 
347  /* Set the ASID for TLB tagging for this context */
348  SET_CONTEXTIDR_ASID(drvdata->base, c->num, c->num);
349 
350  /* Set security bit override to be Non-secure */
351  SET_NSCFG(drvdata->base, mid, 3);
352  }
353 
354  if (drvdata->clk)
355  clk_disable(drvdata->clk);
356  clk_disable(drvdata->pclk);
357 
358  dev_info(&pdev->dev, "context %s using bank %d\n", c->name, c->num);
359  return 0;
360 fail:
361  kfree(ctx_drvdata);
362  return ret;
363 }
364 
365 static int msm_iommu_ctx_remove(struct platform_device *pdev)
366 {
367  struct msm_iommu_ctx_drvdata *drv = NULL;
368  drv = platform_get_drvdata(pdev);
369  if (drv) {
370  memset(drv, 0, sizeof(struct msm_iommu_ctx_drvdata));
371  kfree(drv);
372  platform_set_drvdata(pdev, NULL);
373  }
374  return 0;
375 }
376 
377 static struct platform_driver msm_iommu_driver = {
378  .driver = {
379  .name = "msm_iommu",
380  },
381  .probe = msm_iommu_probe,
382  .remove = msm_iommu_remove,
383 };
384 
385 static struct platform_driver msm_iommu_ctx_driver = {
386  .driver = {
387  .name = "msm_iommu_ctx",
388  },
389  .probe = msm_iommu_ctx_probe,
390  .remove = msm_iommu_ctx_remove,
391 };
392 
393 static int __init msm_iommu_driver_init(void)
394 {
395  int ret;
396  ret = platform_driver_register(&msm_iommu_driver);
397  if (ret != 0) {
398  pr_err("Failed to register IOMMU driver\n");
399  goto error;
400  }
401 
402  ret = platform_driver_register(&msm_iommu_ctx_driver);
403  if (ret != 0) {
404  pr_err("Failed to register IOMMU context driver\n");
405  goto error;
406  }
407 
408 error:
409  return ret;
410 }
411 
412 static void __exit msm_iommu_driver_exit(void)
413 {
414  platform_driver_unregister(&msm_iommu_ctx_driver);
415  platform_driver_unregister(&msm_iommu_driver);
416 }
417 
418 subsys_initcall(msm_iommu_driver_init);
419 module_exit(msm_iommu_driver_exit);
420 
421 MODULE_LICENSE("GPL v2");
422 MODULE_AUTHOR("Stepan Moskovchenko <[email protected]>");