blob: f37e6195bfbf343b13013b9c7745295375cf7268 [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;
Mitchel Humpherysa1830b32013-02-25 18:29:54 -080037 int ret;
Stepan Moskovchenko880a3182012-10-01 12:35:24 -070038
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
Stepan Moskovchenko880a3182012-10-01 12:35:24 -070083 drvdata->bfb_settings = bfb_settings;
84 return 0;
85}
86
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -080087static int msm_iommu_parse_dt(struct platform_device *pdev,
88 struct msm_iommu_drvdata *drvdata)
89{
90 struct device_node *child;
Stepan Moskovchenko4575bdd2012-06-28 14:59:00 -070091 int ret = 0;
Olav Haugan3c7fb382013-01-02 17:32:25 -080092 struct resource *r;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -080093
Stephen Boyd55742b72012-08-08 11:40:26 -070094 drvdata->dev = &pdev->dev;
95 msm_iommu_add_drv(drvdata);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -080096
Stepan Moskovchenko880a3182012-10-01 12:35:24 -070097 ret = msm_iommu_parse_bfb_settings(pdev, drvdata);
98 if (ret)
99 goto fail;
100
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800101 for_each_child_of_node(pdev->dev.of_node, child) {
102 drvdata->ncb++;
103 if (!of_platform_device_create(child, NULL, &pdev->dev))
104 pr_err("Failed to create %s device\n", child->name);
105 }
106
Olav Haugane6c00c32013-01-08 14:11:55 -0800107 ret = of_property_read_string(pdev->dev.of_node, "label",
108 &drvdata->name);
109 if (ret)
110 goto fail;
111
Laura Abbott0d135652012-10-04 12:59:03 -0700112 drvdata->sec_id = -1;
113 of_property_read_u32(pdev->dev.of_node, "qcom,iommu-secure-id",
114 &drvdata->sec_id);
Olav Haugan3c7fb382013-01-02 17:32:25 -0800115
116 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clk_base");
117 if (r) {
118 drvdata->clk_reg_virt = devm_ioremap(&pdev->dev, r->start,
119 resource_size(r));
120 if (!drvdata->clk_reg_virt) {
121 pr_err("Failed to map 0x%x for iommu clk\n",
122 r->start);
123 ret = -ENOMEM;
124 goto fail;
125 }
126 }
127
Olav Haugancd932192013-01-31 18:30:15 -0800128 drvdata->halt_enabled = of_property_read_bool(pdev->dev.of_node,
129 "qcom,iommu-enable-halt");
130
Laura Abbott0d135652012-10-04 12:59:03 -0700131 return 0;
Stepan Moskovchenko4575bdd2012-06-28 14:59:00 -0700132fail:
133 return ret;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800134}
135
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800136static int msm_iommu_pmon_parse_dt(struct platform_device *pdev,
Olav Haugan0c2d9322013-01-31 18:35:30 -0800137 struct iommu_pmon *pmon_info)
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800138{
139 int ret = 0;
140 int irq = platform_get_irq(pdev, 0);
Olav Haugan0c2d9322013-01-31 18:35:30 -0800141 unsigned int cls_prop_size;
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800142
143 if (irq > 0) {
Olav Haugan0c2d9322013-01-31 18:35:30 -0800144 pmon_info->iommu.evt_irq = platform_get_irq(pdev, 0);
145
146 ret = of_property_read_u32(pdev->dev.of_node,
147 "qcom,iommu-pmu-ngroups",
148 &pmon_info->num_groups);
149 if (ret) {
150 pr_err("Error reading qcom,iommu-pmu-ngroups\n");
151 goto fail;
152 }
153 ret = of_property_read_u32(pdev->dev.of_node,
154 "qcom,iommu-pmu-ncounters",
155 &pmon_info->num_counters);
156 if (ret) {
157 pr_err("Error reading qcom,iommu-pmu-ncounters\n");
158 goto fail;
159 }
160
161 if (!of_get_property(pdev->dev.of_node,
162 "qcom,iommu-pmu-event-classes",
163 &cls_prop_size)) {
164 pr_err("Error reading qcom,iommu-pmu-event-classes\n");
165 return -EINVAL;
166 }
167
168 pmon_info->event_cls_supported =
169 devm_kzalloc(&pdev->dev, cls_prop_size, GFP_KERNEL);
170
171 if (!pmon_info->event_cls_supported) {
172 pr_err("Unable to get memory for event class array\n");
173 return -ENOMEM;
174 }
175
176 pmon_info->nevent_cls_supported = cls_prop_size / sizeof(u32);
177
178 ret = of_property_read_u32_array(pdev->dev.of_node,
179 "qcom,iommu-pmu-event-classes",
180 pmon_info->event_cls_supported,
181 pmon_info->nevent_cls_supported);
182 if (ret) {
183 pr_err("Error reading qcom,iommu-pmu-event-classes\n");
184 return ret;
185 }
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800186 } else {
Olav Haugan0c2d9322013-01-31 18:35:30 -0800187 pmon_info->iommu.evt_irq = -1;
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800188 ret = irq;
189 }
Olav Haugan0c2d9322013-01-31 18:35:30 -0800190
191fail:
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800192 return ret;
193}
194
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800195static int __devinit msm_iommu_probe(struct platform_device *pdev)
196{
Olav Haugan0c2d9322013-01-31 18:35:30 -0800197 struct iommu_pmon *pmon_info;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800198 struct msm_iommu_drvdata *drvdata;
199 struct resource *r;
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700200 int ret, needs_alt_core_clk;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800201
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800202 drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
203 if (!drvdata)
204 return -ENOMEM;
205
Olav Haugan3c7fb382013-01-02 17:32:25 -0800206 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iommu_base");
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800207 if (!r)
208 return -EINVAL;
209
210 drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
211 if (!drvdata->base)
212 return -ENOMEM;
213
Olav Haugan95d24162012-12-05 14:47:47 -0800214 drvdata->glb_base = drvdata->base;
215
Stepan Moskovchenko6751acc2012-06-21 17:36:47 -0700216 drvdata->gdsc = devm_regulator_get(&pdev->dev, "vdd");
217 if (IS_ERR(drvdata->gdsc))
218 return -EINVAL;
219
Olav Haugan2648d972013-01-07 17:32:31 -0800220 drvdata->alt_gdsc = devm_regulator_get(&pdev->dev, "qcom,alt-vdd");
221 if (IS_ERR(drvdata->alt_gdsc))
222 drvdata->alt_gdsc = NULL;
223
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700224 drvdata->pclk = devm_clk_get(&pdev->dev, "iface_clk");
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800225 if (IS_ERR(drvdata->pclk))
226 return PTR_ERR(drvdata->pclk);
227
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700228 drvdata->clk = devm_clk_get(&pdev->dev, "core_clk");
229 if (IS_ERR(drvdata->clk))
230 return PTR_ERR(drvdata->clk);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800231
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700232 needs_alt_core_clk = of_property_read_bool(pdev->dev.of_node,
233 "qcom,needs-alt-core-clk");
234 if (needs_alt_core_clk) {
235 drvdata->aclk = devm_clk_get(&pdev->dev, "alt_core_clk");
236 if (IS_ERR(drvdata->aclk))
237 return PTR_ERR(drvdata->aclk);
238 }
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800239
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700240 if (clk_get_rate(drvdata->clk) == 0) {
Olav Haugan8d9dfdd2013-02-12 15:00:13 -0800241 ret = clk_round_rate(drvdata->clk, 1000);
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700242 clk_set_rate(drvdata->clk, ret);
243 }
244
245 if (drvdata->aclk && clk_get_rate(drvdata->aclk) == 0) {
Olav Haugan8d9dfdd2013-02-12 15:00:13 -0800246 ret = clk_round_rate(drvdata->aclk, 1000);
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700247 clk_set_rate(drvdata->aclk, ret);
248 }
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800249
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800250 ret = msm_iommu_parse_dt(pdev, drvdata);
251 if (ret)
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700252 return ret;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800253
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800254 dev_info(&pdev->dev, "device %s mapped at %p, with %d ctx banks\n",
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800255 drvdata->name, drvdata->base, drvdata->ncb);
256
257 platform_set_drvdata(pdev, drvdata);
258
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800259 pmon_info = msm_iommu_pm_alloc(&pdev->dev);
260 if (pmon_info != NULL) {
261 ret = msm_iommu_pmon_parse_dt(pdev, pmon_info);
262 if (ret) {
263 msm_iommu_pm_free(&pdev->dev);
264 pr_info("%s: pmon not available.\n", drvdata->name);
265 } else {
Olav Haugan0c2d9322013-01-31 18:35:30 -0800266 pmon_info->iommu.base = drvdata->base;
Olav Hauganef69e892013-02-04 13:47:08 -0800267 pmon_info->iommu.ops = &iommu_access_ops_v1;
268 pmon_info->iommu.hw_ops = iommu_pm_get_hw_ops_v1();
Olav Haugan0c2d9322013-01-31 18:35:30 -0800269 pmon_info->iommu.iommu_name = drvdata->name;
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800270 ret = msm_iommu_pm_iommu_register(pmon_info);
271 if (ret) {
272 pr_err("%s iommu register fail\n",
273 drvdata->name);
274 msm_iommu_pm_free(&pdev->dev);
275 } else {
276 pr_debug("%s iommu registered for pmon\n",
Olav Haugan0c2d9322013-01-31 18:35:30 -0800277 pmon_info->iommu.iommu_name);
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800278 }
279 }
280 }
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800281 return 0;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800282}
283
284static int __devexit msm_iommu_remove(struct platform_device *pdev)
285{
286 struct msm_iommu_drvdata *drv = NULL;
287
Olav Haugan5ebfbc62013-01-07 17:49:10 -0800288 msm_iommu_pm_iommu_unregister(&pdev->dev);
289 msm_iommu_pm_free(&pdev->dev);
290
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800291 drv = platform_get_drvdata(pdev);
292 if (drv) {
Stephen Boyd55742b72012-08-08 11:40:26 -0700293 msm_iommu_remove_drv(drv);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800294 if (drv->clk)
295 clk_put(drv->clk);
296 clk_put(drv->pclk);
297 platform_set_drvdata(pdev, NULL);
298 }
299 return 0;
300}
301
302static int msm_iommu_ctx_parse_dt(struct platform_device *pdev,
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800303 struct msm_iommu_ctx_drvdata *ctx_drvdata)
304{
305 struct resource *r, rp;
Sathish Ambleycf045e62012-06-07 12:56:50 -0700306 int irq, ret;
Stepan Moskovchenko4575bdd2012-06-28 14:59:00 -0700307 u32 nsid;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800308
Olav Hauganddd379d2013-01-28 09:07:51 -0800309 ctx_drvdata->secure_context = of_property_read_bool(pdev->dev.of_node,
310 "qcom,secure-context");
311
312 if (!ctx_drvdata->secure_context) {
313 irq = platform_get_irq(pdev, 0);
314 if (irq > 0) {
315 ret = request_threaded_irq(irq, NULL,
316 msm_iommu_fault_handler_v2,
317 IRQF_ONESHOT | IRQF_SHARED,
318 "msm_iommu_nonsecure_irq", pdev);
319 if (ret) {
320 pr_err("Request IRQ %d failed with ret=%d\n",
321 irq, ret);
322 return ret;
323 }
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800324 }
325 }
326
327 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
328 if (!r)
329 return -EINVAL;
330
331 ret = of_address_to_resource(pdev->dev.parent->of_node, 0, &rp);
332 if (ret)
333 return -EINVAL;
334
335 /* Calculate the context bank number using the base addresses. The
336 * first 8 pages belong to the global address space which is followed
337 * by the context banks, hence subtract by 8 to get the context bank
338 * number.
339 */
340 ctx_drvdata->num = ((r->start - rp.start) >> CTX_SHIFT) - 8;
341
Stepan Moskovchenkoce78fc22012-07-11 17:20:27 -0700342 if (of_property_read_string(pdev->dev.of_node, "label",
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800343 &ctx_drvdata->name))
344 ctx_drvdata->name = dev_name(&pdev->dev);
345
Stepan Moskovchenko4575bdd2012-06-28 14:59:00 -0700346 if (!of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-sids", &nsid))
347 return -EINVAL;
348
349 if (nsid >= sizeof(ctx_drvdata->sids))
350 return -EINVAL;
351
352 if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-sids",
353 ctx_drvdata->sids,
354 nsid / sizeof(*ctx_drvdata->sids))) {
355 return -EINVAL;
356 }
357 ctx_drvdata->nsid = nsid;
358
Olav Haugan26ddd432012-12-07 11:39:21 -0800359 ctx_drvdata->asid = -1;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800360 return 0;
361}
362
363static int __devinit msm_iommu_ctx_probe(struct platform_device *pdev)
364{
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800365 struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL;
366 int ret;
367
368 if (!pdev->dev.parent)
369 return -EINVAL;
370
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800371 ctx_drvdata = devm_kzalloc(&pdev->dev, sizeof(*ctx_drvdata),
372 GFP_KERNEL);
373 if (!ctx_drvdata)
374 return -ENOMEM;
375
376 ctx_drvdata->pdev = pdev;
377 INIT_LIST_HEAD(&ctx_drvdata->attached_elm);
378 platform_set_drvdata(pdev, ctx_drvdata);
379
Sathish Ambleycf045e62012-06-07 12:56:50 -0700380 ret = msm_iommu_ctx_parse_dt(pdev, ctx_drvdata);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800381 if (!ret)
382 dev_info(&pdev->dev, "context %s using bank %d\n",
Stepan Moskovchenkoe14ca5c2012-07-11 12:40:51 -0700383 ctx_drvdata->name, ctx_drvdata->num);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800384
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800385 return ret;
386}
387
388static int __devexit msm_iommu_ctx_remove(struct platform_device *pdev)
389{
390 platform_set_drvdata(pdev, NULL);
391 return 0;
392}
393
394static struct of_device_id msm_iommu_match_table[] = {
Olav Haugan0e22c482013-01-28 17:39:36 -0800395 { .compatible = "qcom,msm-smmu-v1", },
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800396 {}
397};
398
399static struct platform_driver msm_iommu_driver = {
400 .driver = {
Olav Haugan0e22c482013-01-28 17:39:36 -0800401 .name = "msm_iommu_v1",
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800402 .of_match_table = msm_iommu_match_table,
403 },
404 .probe = msm_iommu_probe,
405 .remove = __devexit_p(msm_iommu_remove),
406};
407
408static struct of_device_id msm_iommu_ctx_match_table[] = {
409 { .name = "qcom,iommu-ctx", },
410 {}
411};
412
413static struct platform_driver msm_iommu_ctx_driver = {
414 .driver = {
Olav Haugan0e22c482013-01-28 17:39:36 -0800415 .name = "msm_iommu_ctx_v1",
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800416 .of_match_table = msm_iommu_ctx_match_table,
417 },
418 .probe = msm_iommu_ctx_probe,
419 .remove = __devexit_p(msm_iommu_ctx_remove),
420};
421
422static int __init msm_iommu_driver_init(void)
423{
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800424 int ret;
425
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800426 ret = platform_driver_register(&msm_iommu_driver);
427 if (ret != 0) {
428 pr_err("Failed to register IOMMU driver\n");
429 goto error;
430 }
431
432 ret = platform_driver_register(&msm_iommu_ctx_driver);
433 if (ret != 0) {
434 pr_err("Failed to register IOMMU context driver\n");
435 goto error;
436 }
437
438error:
439 return ret;
440}
441
442static void __exit msm_iommu_driver_exit(void)
443{
444 platform_driver_unregister(&msm_iommu_ctx_driver);
445 platform_driver_unregister(&msm_iommu_driver);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800446}
447
448subsys_initcall(msm_iommu_driver_init);
449module_exit(msm_iommu_driver_exit);
450
451MODULE_LICENSE("GPL v2");