Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/arm/mach-msm/iommu_dev.c
10817 views
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
34
struct iommu_ctx_iter_data {
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
}
86
EXPORT_SYMBOL(msm_iommu_get_ctx);
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;
137
resource_size_t len;
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]>");
423
424