blob: 02fd133ae66b93a5ae7c77466b5ea9d5ba7777f6 [file] [log] [blame]
Olav Haugan3c7fb382013-01-02 17:32:25 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -08002 *
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
13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/platform_device.h>
18#include <linux/io.h>
19#include <linux/clk.h>
20#include <linux/iommu.h>
21#include <linux/interrupt.h>
22#include <linux/err.h>
23#include <linux/slab.h>
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -080024#include <linux/of.h>
25#include <linux/of_address.h>
26#include <linux/of_device.h>
27
Olav Haugane6d01ef2013-01-25 16:55:44 -080028#include <mach/iommu_hw-v1.h>
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -080029#include <mach/iommu.h>
Olav Haugan5ebfbc62013-01-07 17:49:10 -080030#include <mach/iommu_perfmon.h>
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -080031
Stepan Moskovchenko880a3182012-10-01 12:35:24 -070032static int msm_iommu_parse_bfb_settings(struct platform_device *pdev,
33 struct msm_iommu_drvdata *drvdata)
34{
35 struct msm_iommu_bfb_settings *bfb_settings;
36 u32 nreg, nval;
37 int ret, i;
38
39 /*
40 * It is not valid for a device to have the qcom,iommu-bfb-regs
41 * property but not the qcom,iommu-bfb-data property, and vice versa.
42 */
43 if (!of_get_property(pdev->dev.of_node, "qcom,iommu-bfb-regs", &nreg)) {
44 if (of_get_property(pdev->dev.of_node, "qcom,iommu-bfb-data",
45 &nval))
46 return -EINVAL;
47 return 0;
48 }
49
50 if (!of_get_property(pdev->dev.of_node, "qcom,iommu-bfb-data", &nval))
51 return -EINVAL;
52
53 if (nreg >= sizeof(bfb_settings->regs))
54 return -EINVAL;
55
56 if (nval >= sizeof(bfb_settings->data))
57 return -EINVAL;
58
59 if (nval != nreg)
60 return -EINVAL;
61
62 bfb_settings = devm_kzalloc(&pdev->dev, sizeof(*bfb_settings),
63 GFP_KERNEL);
64 if (!bfb_settings)
65 return -ENOMEM;
66
67 ret = of_property_read_u32_array(pdev->dev.of_node,
68 "qcom,iommu-bfb-regs",
69 bfb_settings->regs,
70 nreg / sizeof(*bfb_settings->regs));
71 if (ret)
72 return ret;
73
74 ret = of_property_read_u32_array(pdev->dev.of_node,
75 "qcom,iommu-bfb-data",
76 bfb_settings->data,
77 nval / sizeof(*bfb_settings->data));
78 if (ret)
79 return ret;
80
81 bfb_settings->length = nreg / sizeof(*bfb_settings->regs);
82
83 for (i = 0; i < bfb_settings->length; i++)
84 if (bfb_settings->regs[i] < IMPLDEF_OFFSET ||
85 bfb_settings->regs[i] >= IMPLDEF_OFFSET + IMPLDEF_LENGTH)
86 return -EINVAL;
87
88 drvdata->bfb_settings = bfb_settings;
89 return 0;
90}
91
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -080092static int msm_iommu_parse_dt(struct platform_device *pdev,
93 struct msm_iommu_drvdata *drvdata)
94{
95 struct device_node *child;
Stepan Moskovchenko4575bdd2012-06-28 14:59:00 -070096 int ret = 0;
Olav Haugan3c7fb382013-01-02 17:32:25 -080097 struct resource *r;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -080098
Stephen Boyd55742b72012-08-08 11:40:26 -070099 drvdata->dev = &pdev->dev;
100 msm_iommu_add_drv(drvdata);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800101
Stepan Moskovchenko880a3182012-10-01 12:35:24 -0700102 ret = msm_iommu_parse_bfb_settings(pdev, drvdata);
103 if (ret)
104 goto fail;
105
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800106 for_each_child_of_node(pdev->dev.of_node, child) {
107 drvdata->ncb++;
108 if (!of_platform_device_create(child, NULL, &pdev->dev))
109 pr_err("Failed to create %s device\n", child->name);
110 }
111
Olav Haugane6c00c32013-01-08 14:11:55 -0800112 ret = of_property_read_string(pdev->dev.of_node, "label",
113 &drvdata->name);
114 if (ret)
115 goto fail;
116
Laura Abbott0d135652012-10-04 12:59:03 -0700117 drvdata->sec_id = -1;
118 of_property_read_u32(pdev->dev.of_node, "qcom,iommu-secure-id",
119 &drvdata->sec_id);
Olav Haugan3c7fb382013-01-02 17:32:25 -0800120
121 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clk_base");
122 if (r) {
123 drvdata->clk_reg_virt = devm_ioremap(&pdev->dev, r->start,
124 resource_size(r));
125 if (!drvdata->clk_reg_virt) {
126 pr_err("Failed to map 0x%x for iommu clk\n",
127 r->start);
128 ret = -ENOMEM;
129 goto fail;
130 }
131 }
132
Olav Haugancd932192013-01-31 18:30:15 -0800133 drvdata->halt_enabled = of_property_read_bool(pdev->dev.of_node,
134 "qcom,iommu-enable-halt");
135
Laura Abbott0d135652012-10-04 12:59:03 -0700136 return 0;
Stepan Moskovchenko4575bdd2012-06-28 14:59:00 -0700137fail:
138 return ret;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800139}
140
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800141static int msm_iommu_pmon_parse_dt(struct platform_device *pdev,
Olav Haugan0c2d9322013-01-31 18:35:30 -0800142 struct iommu_pmon *pmon_info)
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800143{
144 int ret = 0;
145 int irq = platform_get_irq(pdev, 0);
Olav Haugan0c2d9322013-01-31 18:35:30 -0800146 unsigned int cls_prop_size;
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800147
148 if (irq > 0) {
Olav Haugan0c2d9322013-01-31 18:35:30 -0800149 pmon_info->iommu.evt_irq = platform_get_irq(pdev, 0);
150
151 ret = of_property_read_u32(pdev->dev.of_node,
152 "qcom,iommu-pmu-ngroups",
153 &pmon_info->num_groups);
154 if (ret) {
155 pr_err("Error reading qcom,iommu-pmu-ngroups\n");
156 goto fail;
157 }
158 ret = of_property_read_u32(pdev->dev.of_node,
159 "qcom,iommu-pmu-ncounters",
160 &pmon_info->num_counters);
161 if (ret) {
162 pr_err("Error reading qcom,iommu-pmu-ncounters\n");
163 goto fail;
164 }
165
166 if (!of_get_property(pdev->dev.of_node,
167 "qcom,iommu-pmu-event-classes",
168 &cls_prop_size)) {
169 pr_err("Error reading qcom,iommu-pmu-event-classes\n");
170 return -EINVAL;
171 }
172
173 pmon_info->event_cls_supported =
174 devm_kzalloc(&pdev->dev, cls_prop_size, GFP_KERNEL);
175
176 if (!pmon_info->event_cls_supported) {
177 pr_err("Unable to get memory for event class array\n");
178 return -ENOMEM;
179 }
180
181 pmon_info->nevent_cls_supported = cls_prop_size / sizeof(u32);
182
183 ret = of_property_read_u32_array(pdev->dev.of_node,
184 "qcom,iommu-pmu-event-classes",
185 pmon_info->event_cls_supported,
186 pmon_info->nevent_cls_supported);
187 if (ret) {
188 pr_err("Error reading qcom,iommu-pmu-event-classes\n");
189 return ret;
190 }
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800191 } else {
Olav Haugan0c2d9322013-01-31 18:35:30 -0800192 pmon_info->iommu.evt_irq = -1;
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800193 ret = irq;
194 }
Olav Haugan0c2d9322013-01-31 18:35:30 -0800195
196fail:
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800197 return ret;
198}
199
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800200static int __devinit msm_iommu_probe(struct platform_device *pdev)
201{
Olav Haugan0c2d9322013-01-31 18:35:30 -0800202 struct iommu_pmon *pmon_info;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800203 struct msm_iommu_drvdata *drvdata;
204 struct resource *r;
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700205 int ret, needs_alt_core_clk;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800206
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800207 drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
208 if (!drvdata)
209 return -ENOMEM;
210
Olav Haugan3c7fb382013-01-02 17:32:25 -0800211 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iommu_base");
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800212 if (!r)
213 return -EINVAL;
214
215 drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
216 if (!drvdata->base)
217 return -ENOMEM;
218
Olav Haugan95d24162012-12-05 14:47:47 -0800219 drvdata->glb_base = drvdata->base;
220
Stepan Moskovchenko6751acc2012-06-21 17:36:47 -0700221 drvdata->gdsc = devm_regulator_get(&pdev->dev, "vdd");
222 if (IS_ERR(drvdata->gdsc))
223 return -EINVAL;
224
Olav Haugan2648d972013-01-07 17:32:31 -0800225 drvdata->alt_gdsc = devm_regulator_get(&pdev->dev, "qcom,alt-vdd");
226 if (IS_ERR(drvdata->alt_gdsc))
227 drvdata->alt_gdsc = NULL;
228
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700229 drvdata->pclk = devm_clk_get(&pdev->dev, "iface_clk");
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800230 if (IS_ERR(drvdata->pclk))
231 return PTR_ERR(drvdata->pclk);
232
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700233 drvdata->clk = devm_clk_get(&pdev->dev, "core_clk");
234 if (IS_ERR(drvdata->clk))
235 return PTR_ERR(drvdata->clk);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800236
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700237 needs_alt_core_clk = of_property_read_bool(pdev->dev.of_node,
238 "qcom,needs-alt-core-clk");
239 if (needs_alt_core_clk) {
240 drvdata->aclk = devm_clk_get(&pdev->dev, "alt_core_clk");
241 if (IS_ERR(drvdata->aclk))
242 return PTR_ERR(drvdata->aclk);
243 }
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800244
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700245 if (clk_get_rate(drvdata->clk) == 0) {
Olav Haugan8d9dfdd2013-02-12 15:00:13 -0800246 ret = clk_round_rate(drvdata->clk, 1000);
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700247 clk_set_rate(drvdata->clk, ret);
248 }
249
250 if (drvdata->aclk && clk_get_rate(drvdata->aclk) == 0) {
Olav Haugan8d9dfdd2013-02-12 15:00:13 -0800251 ret = clk_round_rate(drvdata->aclk, 1000);
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700252 clk_set_rate(drvdata->aclk, ret);
253 }
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800254
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800255 ret = msm_iommu_parse_dt(pdev, drvdata);
256 if (ret)
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700257 return ret;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800258
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800259 dev_info(&pdev->dev, "device %s mapped at %p, with %d ctx banks\n",
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800260 drvdata->name, drvdata->base, drvdata->ncb);
261
262 platform_set_drvdata(pdev, drvdata);
263
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800264 pmon_info = msm_iommu_pm_alloc(&pdev->dev);
265 if (pmon_info != NULL) {
266 ret = msm_iommu_pmon_parse_dt(pdev, pmon_info);
267 if (ret) {
268 msm_iommu_pm_free(&pdev->dev);
269 pr_info("%s: pmon not available.\n", drvdata->name);
270 } else {
Olav Haugan0c2d9322013-01-31 18:35:30 -0800271 pmon_info->iommu.base = drvdata->base;
272 pmon_info->iommu.ops = &iommu_access_ops;
273 pmon_info->iommu.iommu_name = drvdata->name;
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800274 ret = msm_iommu_pm_iommu_register(pmon_info);
275 if (ret) {
276 pr_err("%s iommu register fail\n",
277 drvdata->name);
278 msm_iommu_pm_free(&pdev->dev);
279 } else {
280 pr_debug("%s iommu registered for pmon\n",
Olav Haugan0c2d9322013-01-31 18:35:30 -0800281 pmon_info->iommu.iommu_name);
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800282 }
283 }
284 }
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800285 return 0;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800286}
287
288static int __devexit msm_iommu_remove(struct platform_device *pdev)
289{
290 struct msm_iommu_drvdata *drv = NULL;
291
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800292 msm_iommu_pm_iommu_unregister(&pdev->dev);
293 msm_iommu_pm_free(&pdev->dev);
294
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800295 drv = platform_get_drvdata(pdev);
296 if (drv) {
Stephen Boyd55742b72012-08-08 11:40:26 -0700297 msm_iommu_remove_drv(drv);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800298 if (drv->clk)
299 clk_put(drv->clk);
300 clk_put(drv->pclk);
301 platform_set_drvdata(pdev, NULL);
302 }
303 return 0;
304}
305
306static int msm_iommu_ctx_parse_dt(struct platform_device *pdev,
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800307 struct msm_iommu_ctx_drvdata *ctx_drvdata)
308{
309 struct resource *r, rp;
Sathish Ambleycf045e62012-06-07 12:56:50 -0700310 int irq, ret;
Stepan Moskovchenko4575bdd2012-06-28 14:59:00 -0700311 u32 nsid;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800312
Olav Hauganddd379d2013-01-28 09:07:51 -0800313 ctx_drvdata->secure_context = of_property_read_bool(pdev->dev.of_node,
314 "qcom,secure-context");
315
316 if (!ctx_drvdata->secure_context) {
317 irq = platform_get_irq(pdev, 0);
318 if (irq > 0) {
319 ret = request_threaded_irq(irq, NULL,
320 msm_iommu_fault_handler_v2,
321 IRQF_ONESHOT | IRQF_SHARED,
322 "msm_iommu_nonsecure_irq", pdev);
323 if (ret) {
324 pr_err("Request IRQ %d failed with ret=%d\n",
325 irq, ret);
326 return ret;
327 }
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800328 }
329 }
330
331 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
332 if (!r)
333 return -EINVAL;
334
335 ret = of_address_to_resource(pdev->dev.parent->of_node, 0, &rp);
336 if (ret)
337 return -EINVAL;
338
339 /* Calculate the context bank number using the base addresses. The
340 * first 8 pages belong to the global address space which is followed
341 * by the context banks, hence subtract by 8 to get the context bank
342 * number.
343 */
344 ctx_drvdata->num = ((r->start - rp.start) >> CTX_SHIFT) - 8;
345
Stepan Moskovchenkoce78fc22012-07-11 17:20:27 -0700346 if (of_property_read_string(pdev->dev.of_node, "label",
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800347 &ctx_drvdata->name))
348 ctx_drvdata->name = dev_name(&pdev->dev);
349
Stepan Moskovchenko4575bdd2012-06-28 14:59:00 -0700350 if (!of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-sids", &nsid))
351 return -EINVAL;
352
353 if (nsid >= sizeof(ctx_drvdata->sids))
354 return -EINVAL;
355
356 if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-sids",
357 ctx_drvdata->sids,
358 nsid / sizeof(*ctx_drvdata->sids))) {
359 return -EINVAL;
360 }
361 ctx_drvdata->nsid = nsid;
362
Olav Haugan26ddd432012-12-07 11:39:21 -0800363 ctx_drvdata->asid = -1;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800364 return 0;
365}
366
367static int __devinit msm_iommu_ctx_probe(struct platform_device *pdev)
368{
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800369 struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL;
370 int ret;
371
372 if (!pdev->dev.parent)
373 return -EINVAL;
374
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800375 ctx_drvdata = devm_kzalloc(&pdev->dev, sizeof(*ctx_drvdata),
376 GFP_KERNEL);
377 if (!ctx_drvdata)
378 return -ENOMEM;
379
380 ctx_drvdata->pdev = pdev;
381 INIT_LIST_HEAD(&ctx_drvdata->attached_elm);
382 platform_set_drvdata(pdev, ctx_drvdata);
383
Sathish Ambleycf045e62012-06-07 12:56:50 -0700384 ret = msm_iommu_ctx_parse_dt(pdev, ctx_drvdata);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800385 if (!ret)
386 dev_info(&pdev->dev, "context %s using bank %d\n",
Stepan Moskovchenkoe14ca5c2012-07-11 12:40:51 -0700387 ctx_drvdata->name, ctx_drvdata->num);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800388
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800389 return ret;
390}
391
392static int __devexit msm_iommu_ctx_remove(struct platform_device *pdev)
393{
394 platform_set_drvdata(pdev, NULL);
395 return 0;
396}
397
398static struct of_device_id msm_iommu_match_table[] = {
Olav Haugan0e22c482013-01-28 17:39:36 -0800399 { .compatible = "qcom,msm-smmu-v1", },
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800400 {}
401};
402
403static struct platform_driver msm_iommu_driver = {
404 .driver = {
Olav Haugan0e22c482013-01-28 17:39:36 -0800405 .name = "msm_iommu_v1",
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800406 .of_match_table = msm_iommu_match_table,
407 },
408 .probe = msm_iommu_probe,
409 .remove = __devexit_p(msm_iommu_remove),
410};
411
412static struct of_device_id msm_iommu_ctx_match_table[] = {
413 { .name = "qcom,iommu-ctx", },
414 {}
415};
416
417static struct platform_driver msm_iommu_ctx_driver = {
418 .driver = {
Olav Haugan0e22c482013-01-28 17:39:36 -0800419 .name = "msm_iommu_ctx_v1",
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800420 .of_match_table = msm_iommu_ctx_match_table,
421 },
422 .probe = msm_iommu_ctx_probe,
423 .remove = __devexit_p(msm_iommu_ctx_remove),
424};
425
426static int __init msm_iommu_driver_init(void)
427{
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800428 int ret;
429
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800430 ret = platform_driver_register(&msm_iommu_driver);
431 if (ret != 0) {
432 pr_err("Failed to register IOMMU driver\n");
433 goto error;
434 }
435
436 ret = platform_driver_register(&msm_iommu_ctx_driver);
437 if (ret != 0) {
438 pr_err("Failed to register IOMMU context driver\n");
439 goto error;
440 }
441
442error:
443 return ret;
444}
445
446static void __exit msm_iommu_driver_exit(void)
447{
448 platform_driver_unregister(&msm_iommu_ctx_driver);
449 platform_driver_unregister(&msm_iommu_driver);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800450}
451
452subsys_initcall(msm_iommu_driver_init);
453module_exit(msm_iommu_driver_exit);
454
455MODULE_LICENSE("GPL v2");