Blame view

drivers/iommu/msm_iommu_dev.c 8.89 KB
b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
1
  /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 and
   * only version 2 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   * 02110-1301, USA.
   */
  
  #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/platform_device.h>
  #include <linux/io.h>
  #include <linux/clk.h>
  #include <linux/iommu.h>
  #include <linux/interrupt.h>
  #include <linux/err.h>
  #include <linux/slab.h>
  
  #include <mach/iommu_hw-8xxx.h>
  #include <mach/iommu.h>
b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
32
  #include <mach/clk.h>
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  
  struct iommu_ctx_iter_data {
  	/* input */
  	const char *name;
  
  	/* output */
  	struct device *dev;
  };
  
  static struct platform_device *msm_iommu_root_dev;
  
  static int each_iommu_ctx(struct device *dev, void *data)
  {
  	struct iommu_ctx_iter_data *res = data;
  	struct msm_iommu_ctx_dev *c = dev->platform_data;
  
  	if (!res || !c || !c->name || !res->name)
  		return -EINVAL;
  
  	if (!strcmp(res->name, c->name)) {
  		res->dev = dev;
  		return 1;
  	}
  	return 0;
  }
  
  static int each_iommu(struct device *dev, void *data)
  {
  	return device_for_each_child(dev, data, each_iommu_ctx);
  }
  
  struct device *msm_iommu_get_ctx(const char *ctx_name)
  {
  	struct iommu_ctx_iter_data r;
  	int found;
  
  	if (!msm_iommu_root_dev) {
  		pr_err("No root IOMMU device.
  ");
  		goto fail;
  	}
  
  	r.name = ctx_name;
  	found = device_for_each_child(&msm_iommu_root_dev->dev, &r, each_iommu);
  
  	if (!found) {
  		pr_err("Could not find context <%s>
  ", ctx_name);
  		goto fail;
  	}
  
  	return r.dev;
  fail:
  	return NULL;
  }
  EXPORT_SYMBOL(msm_iommu_get_ctx);
a43d8c101   Stepan Moskovchenko   msm: iommu: Remov...
89
  static void msm_iommu_reset(void __iomem *base, int ncb)
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
90
  {
a43d8c101   Stepan Moskovchenko   msm: iommu: Remov...
91
  	int ctx;
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
92
93
94
95
96
97
98
99
100
101
102
103
  
  	SET_RPUE(base, 0);
  	SET_RPUEIE(base, 0);
  	SET_ESRRESTORE(base, 0);
  	SET_TBE(base, 0);
  	SET_CR(base, 0);
  	SET_SPDMBE(base, 0);
  	SET_TESTBUSCR(base, 0);
  	SET_TLBRSW(base, 0);
  	SET_GLOBAL_TLBIALL(base, 0);
  	SET_RPU_ACR(base, 0);
  	SET_TLBLKCRWE(base, 1);
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
  
  	for (ctx = 0; ctx < ncb; ctx++) {
  		SET_BPRCOSH(base, ctx, 0);
  		SET_BPRCISH(base, ctx, 0);
  		SET_BPRCNSH(base, ctx, 0);
  		SET_BPSHCFG(base, ctx, 0);
  		SET_BPMTCFG(base, ctx, 0);
  		SET_ACTLR(base, ctx, 0);
  		SET_SCTLR(base, ctx, 0);
  		SET_FSRRESTORE(base, ctx, 0);
  		SET_TTBR0(base, ctx, 0);
  		SET_TTBR1(base, ctx, 0);
  		SET_TTBCR(base, ctx, 0);
  		SET_BFBCR(base, ctx, 0);
  		SET_PAR(base, ctx, 0);
  		SET_FAR(base, ctx, 0);
  		SET_CTX_TLBIALL(base, ctx, 0);
  		SET_TLBFLPTER(base, ctx, 0);
  		SET_TLBSLPTER(base, ctx, 0);
  		SET_TLBLKCR(base, ctx, 0);
  		SET_PRRR(base, ctx, 0);
  		SET_NMRR(base, ctx, 0);
  		SET_CONTEXTIDR(base, ctx, 0);
  	}
  }
  
  static int msm_iommu_probe(struct platform_device *pdev)
  {
a86c44d48   Vasiliy Kulikov   arm: mach-msm: fi...
132
  	struct resource *r, *r2;
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
133
  	struct clk *iommu_clk;
b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
134
  	struct clk *iommu_pclk;
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
135
136
137
138
  	struct msm_iommu_drvdata *drvdata;
  	struct msm_iommu_dev *iommu_dev = pdev->dev.platform_data;
  	void __iomem *regs_base;
  	resource_size_t	len;
a43d8c101   Stepan Moskovchenko   msm: iommu: Remov...
139
  	int ret, irq, par;
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
140

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
141
142
143
144
  	if (pdev->id == -1) {
  		msm_iommu_root_dev = pdev;
  		return 0;
  	}
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
145

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
146
  	drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
147

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
148
149
150
151
152
153
154
155
156
  	if (!drvdata) {
  		ret = -ENOMEM;
  		goto fail;
  	}
  
  	if (!iommu_dev) {
  		ret = -ENODEV;
  		goto fail;
  	}
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
157

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  	iommu_pclk = clk_get(NULL, "smmu_pclk");
  	if (IS_ERR(iommu_pclk)) {
  		ret = -ENODEV;
  		goto fail;
  	}
  
  	ret = clk_enable(iommu_pclk);
  	if (ret)
  		goto fail_enable;
  
  	iommu_clk = clk_get(&pdev->dev, "iommu_clk");
  
  	if (!IS_ERR(iommu_clk))	{
  		if (clk_get_rate(iommu_clk) == 0)
  			clk_set_min_rate(iommu_clk, 1);
  
  		ret = clk_enable(iommu_clk);
  		if (ret) {
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
176
  			clk_put(iommu_clk);
b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
177
  			goto fail_pclk;
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
178
  		}
b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
179
180
  	} else
  		iommu_clk = NULL;
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
181

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
182
  	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "physbase");
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
183

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
184
185
186
187
  	if (!r) {
  		ret = -ENODEV;
  		goto fail_clk;
  	}
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
188

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
189
  	len = resource_size(r);
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
190

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
191
192
193
194
195
196
197
198
  	r2 = request_mem_region(r->start, len, r->name);
  	if (!r2) {
  		pr_err("Could not request memory region: start=%p, len=%d
  ",
  							(void *) r->start, len);
  		ret = -EBUSY;
  		goto fail_clk;
  	}
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
199

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
200
  	regs_base = ioremap(r2->start, len);
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
201

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
202
203
204
205
206
207
208
209
210
211
212
213
214
  	if (!regs_base) {
  		pr_err("Could not ioremap: start=%p, len=%d
  ",
  			 (void *) r2->start, len);
  		ret = -EBUSY;
  		goto fail_mem;
  	}
  
  	irq = platform_get_irq_byname(pdev, "secure_irq");
  	if (irq < 0) {
  		ret = -ENODEV;
  		goto fail_io;
  	}
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
215

a43d8c101   Stepan Moskovchenko   msm: iommu: Remov...
216
  	msm_iommu_reset(regs_base, iommu_dev->ncb);
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
217

a43d8c101   Stepan Moskovchenko   msm: iommu: Remov...
218
219
220
221
222
223
224
225
226
227
228
  	SET_M(regs_base, 0, 1);
  	SET_PAR(regs_base, 0, 0);
  	SET_V2PCFG(regs_base, 0, 1);
  	SET_V2PPR(regs_base, 0, 0);
  	par = GET_PAR(regs_base, 0);
  	SET_V2PCFG(regs_base, 0, 0);
  	SET_M(regs_base, 0, 0);
  
  	if (!par) {
  		pr_err("%s: Invalid PAR value detected
  ", iommu_dev->name);
b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
229
230
231
  		ret = -ENODEV;
  		goto fail_io;
  	}
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
232

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
233
234
235
236
237
238
239
  	ret = request_irq(irq, msm_iommu_fault_handler, 0,
  			"msm_iommu_secure_irpt_handler", drvdata);
  	if (ret) {
  		pr_err("Request IRQ %d failed with ret=%d
  ", irq, ret);
  		goto fail_io;
  	}
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
240

a43d8c101   Stepan Moskovchenko   msm: iommu: Remov...
241

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
242
243
244
245
  	drvdata->pclk = iommu_pclk;
  	drvdata->clk = iommu_clk;
  	drvdata->base = regs_base;
  	drvdata->irq = irq;
a43d8c101   Stepan Moskovchenko   msm: iommu: Remov...
246
  	drvdata->ncb = iommu_dev->ncb;
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
247

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
248
249
  	pr_info("device %s mapped at %p, irq %d with %d ctx banks
  ",
a43d8c101   Stepan Moskovchenko   msm: iommu: Remov...
250
  		iommu_dev->name, regs_base, irq, iommu_dev->ncb);
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
251

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
252
  	platform_set_drvdata(pdev, drvdata);
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
253

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
254
255
256
257
  	if (iommu_clk)
  		clk_disable(iommu_clk);
  
  	clk_disable(iommu_pclk);
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
258

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
259
  	return 0;
a86c44d48   Vasiliy Kulikov   arm: mach-msm: fi...
260
261
262
263
  fail_io:
  	iounmap(regs_base);
  fail_mem:
  	release_mem_region(r->start, len);
b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
264
265
266
267
268
269
270
271
272
  fail_clk:
  	if (iommu_clk) {
  		clk_disable(iommu_clk);
  		clk_put(iommu_clk);
  	}
  fail_pclk:
  	clk_disable(iommu_pclk);
  fail_enable:
  	clk_put(iommu_pclk);
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
273
274
275
276
277
278
279
280
281
282
283
  fail:
  	kfree(drvdata);
  	return ret;
  }
  
  static int msm_iommu_remove(struct platform_device *pdev)
  {
  	struct msm_iommu_drvdata *drv = NULL;
  
  	drv = platform_get_drvdata(pdev);
  	if (drv) {
b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
284
285
286
287
  		if (drv->clk)
  			clk_put(drv->clk);
  		clk_put(drv->pclk);
  		memset(drv, 0, sizeof(*drv));
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
288
289
290
291
292
293
294
295
296
297
298
  		kfree(drv);
  		platform_set_drvdata(pdev, NULL);
  	}
  	return 0;
  }
  
  static int msm_iommu_ctx_probe(struct platform_device *pdev)
  {
  	struct msm_iommu_ctx_dev *c = pdev->dev.platform_data;
  	struct msm_iommu_drvdata *drvdata;
  	struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL;
b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
299
  	int i, ret;
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
  	if (!c || !pdev->dev.parent) {
  		ret = -EINVAL;
  		goto fail;
  	}
  
  	drvdata = dev_get_drvdata(pdev->dev.parent);
  
  	if (!drvdata) {
  		ret = -ENODEV;
  		goto fail;
  	}
  
  	ctx_drvdata = kzalloc(sizeof(*ctx_drvdata), GFP_KERNEL);
  	if (!ctx_drvdata) {
  		ret = -ENOMEM;
  		goto fail;
  	}
  	ctx_drvdata->num = c->num;
  	ctx_drvdata->pdev = pdev;
  
  	INIT_LIST_HEAD(&ctx_drvdata->attached_elm);
  	platform_set_drvdata(pdev, ctx_drvdata);
b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
322
323
324
325
326
327
328
329
330
331
332
  	ret = clk_enable(drvdata->pclk);
  	if (ret)
  		goto fail;
  
  	if (drvdata->clk) {
  		ret = clk_enable(drvdata->clk);
  		if (ret) {
  			clk_disable(drvdata->pclk);
  			goto fail;
  		}
  	}
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
333
334
335
336
337
338
339
340
  	/* Program the M2V tables for this context */
  	for (i = 0; i < MAX_NUM_MIDS; i++) {
  		int mid = c->mids[i];
  		if (mid == -1)
  			break;
  
  		SET_M2VCBR_N(drvdata->base, mid, 0);
  		SET_CBACR_N(drvdata->base, c->num, 0);
2e8c8ba98   Stepan Moskovchenko   msm: iommu: Use A...
341
342
  		/* Set VMID = 0 */
  		SET_VMID(drvdata->base, mid, 0);
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
343
344
345
  
  		/* Set the context number for that MID to this context */
  		SET_CBNDX(drvdata->base, mid, c->num);
2e8c8ba98   Stepan Moskovchenko   msm: iommu: Use A...
346
347
348
349
350
  		/* Set MID associated with this context bank to 0*/
  		SET_CBVMID(drvdata->base, c->num, 0);
  
  		/* Set the ASID for TLB tagging for this context */
  		SET_CONTEXTIDR_ASID(drvdata->base, c->num, c->num);
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
351
352
353
354
  
  		/* Set security bit override to be Non-secure */
  		SET_NSCFG(drvdata->base, mid, 3);
  	}
b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
355
356
357
  	if (drvdata->clk)
  		clk_disable(drvdata->clk);
  	clk_disable(drvdata->pclk);
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
358

b61401adf   Stepan Moskovchenko   msm: iommu: Rewor...
359
360
  	dev_info(&pdev->dev, "context %s using bank %d
  ", c->name, c->num);
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
  	return 0;
  fail:
  	kfree(ctx_drvdata);
  	return ret;
  }
  
  static int msm_iommu_ctx_remove(struct platform_device *pdev)
  {
  	struct msm_iommu_ctx_drvdata *drv = NULL;
  	drv = platform_get_drvdata(pdev);
  	if (drv) {
  		memset(drv, 0, sizeof(struct msm_iommu_ctx_drvdata));
  		kfree(drv);
  		platform_set_drvdata(pdev, NULL);
  	}
  	return 0;
  }
  
  static struct platform_driver msm_iommu_driver = {
  	.driver = {
  		.name	= "msm_iommu",
  	},
  	.probe		= msm_iommu_probe,
  	.remove		= msm_iommu_remove,
  };
  
  static struct platform_driver msm_iommu_ctx_driver = {
  	.driver = {
  		.name	= "msm_iommu_ctx",
  	},
  	.probe		= msm_iommu_ctx_probe,
  	.remove		= msm_iommu_ctx_remove,
  };
516cbc793   Stepan Moskovchenko   msm: iommu: Mark ...
394
  static int __init msm_iommu_driver_init(void)
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  {
  	int ret;
  	ret = platform_driver_register(&msm_iommu_driver);
  	if (ret != 0) {
  		pr_err("Failed to register IOMMU driver
  ");
  		goto error;
  	}
  
  	ret = platform_driver_register(&msm_iommu_ctx_driver);
  	if (ret != 0) {
  		pr_err("Failed to register IOMMU context driver
  ");
  		goto error;
  	}
  
  error:
  	return ret;
  }
516cbc793   Stepan Moskovchenko   msm: iommu: Mark ...
414
  static void __exit msm_iommu_driver_exit(void)
c6a5951ee   Stepan Moskovchenko   msm: Platform ini...
415
416
417
418
419
420
421
422
423
424
  {
  	platform_driver_unregister(&msm_iommu_ctx_driver);
  	platform_driver_unregister(&msm_iommu_driver);
  }
  
  subsys_initcall(msm_iommu_driver_init);
  module_exit(msm_iommu_driver_exit);
  
  MODULE_LICENSE("GPL v2");
  MODULE_AUTHOR("Stepan Moskovchenko <stepanm@codeaurora.org>");