blob: 9b6dc0eb12488d327cdada113309612263442c9c [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
28#include <mach/iommu_hw-v2.h>
29#include <mach/iommu.h>
30
Stepan Moskovchenko880a3182012-10-01 12:35:24 -070031static int msm_iommu_parse_bfb_settings(struct platform_device *pdev,
32 struct msm_iommu_drvdata *drvdata)
33{
34 struct msm_iommu_bfb_settings *bfb_settings;
35 u32 nreg, nval;
36 int ret, i;
37
38 /*
39 * It is not valid for a device to have the qcom,iommu-bfb-regs
40 * property but not the qcom,iommu-bfb-data property, and vice versa.
41 */
42 if (!of_get_property(pdev->dev.of_node, "qcom,iommu-bfb-regs", &nreg)) {
43 if (of_get_property(pdev->dev.of_node, "qcom,iommu-bfb-data",
44 &nval))
45 return -EINVAL;
46 return 0;
47 }
48
49 if (!of_get_property(pdev->dev.of_node, "qcom,iommu-bfb-data", &nval))
50 return -EINVAL;
51
52 if (nreg >= sizeof(bfb_settings->regs))
53 return -EINVAL;
54
55 if (nval >= sizeof(bfb_settings->data))
56 return -EINVAL;
57
58 if (nval != nreg)
59 return -EINVAL;
60
61 bfb_settings = devm_kzalloc(&pdev->dev, sizeof(*bfb_settings),
62 GFP_KERNEL);
63 if (!bfb_settings)
64 return -ENOMEM;
65
66 ret = of_property_read_u32_array(pdev->dev.of_node,
67 "qcom,iommu-bfb-regs",
68 bfb_settings->regs,
69 nreg / sizeof(*bfb_settings->regs));
70 if (ret)
71 return ret;
72
73 ret = of_property_read_u32_array(pdev->dev.of_node,
74 "qcom,iommu-bfb-data",
75 bfb_settings->data,
76 nval / sizeof(*bfb_settings->data));
77 if (ret)
78 return ret;
79
80 bfb_settings->length = nreg / sizeof(*bfb_settings->regs);
81
82 for (i = 0; i < bfb_settings->length; i++)
83 if (bfb_settings->regs[i] < IMPLDEF_OFFSET ||
84 bfb_settings->regs[i] >= IMPLDEF_OFFSET + IMPLDEF_LENGTH)
85 return -EINVAL;
86
87 drvdata->bfb_settings = bfb_settings;
88 return 0;
89}
90
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -080091static int msm_iommu_parse_dt(struct platform_device *pdev,
92 struct msm_iommu_drvdata *drvdata)
93{
94 struct device_node *child;
Stepan Moskovchenko4575bdd2012-06-28 14:59:00 -070095 int ret = 0;
Olav Haugan3c7fb382013-01-02 17:32:25 -080096 struct resource *r;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -080097
Stephen Boyd55742b72012-08-08 11:40:26 -070098 drvdata->dev = &pdev->dev;
99 msm_iommu_add_drv(drvdata);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800100
Stepan Moskovchenko880a3182012-10-01 12:35:24 -0700101 ret = msm_iommu_parse_bfb_settings(pdev, drvdata);
102 if (ret)
103 goto fail;
104
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800105 for_each_child_of_node(pdev->dev.of_node, child) {
106 drvdata->ncb++;
107 if (!of_platform_device_create(child, NULL, &pdev->dev))
108 pr_err("Failed to create %s device\n", child->name);
109 }
110
111 drvdata->name = dev_name(&pdev->dev);
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
Laura Abbott0d135652012-10-04 12:59:03 -0700128 return 0;
Stepan Moskovchenko4575bdd2012-06-28 14:59:00 -0700129fail:
130 return ret;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800131}
132
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800133static int __devinit msm_iommu_probe(struct platform_device *pdev)
134{
135 struct msm_iommu_drvdata *drvdata;
136 struct resource *r;
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700137 int ret, needs_alt_core_clk;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800138
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800139 drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
140 if (!drvdata)
141 return -ENOMEM;
142
Olav Haugan3c7fb382013-01-02 17:32:25 -0800143 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iommu_base");
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800144 if (!r)
145 return -EINVAL;
146
147 drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
148 if (!drvdata->base)
149 return -ENOMEM;
150
Olav Haugan95d24162012-12-05 14:47:47 -0800151 drvdata->glb_base = drvdata->base;
152
Stepan Moskovchenko6751acc2012-06-21 17:36:47 -0700153 drvdata->gdsc = devm_regulator_get(&pdev->dev, "vdd");
154 if (IS_ERR(drvdata->gdsc))
155 return -EINVAL;
156
Olav Haugan2648d972013-01-07 17:32:31 -0800157 drvdata->alt_gdsc = devm_regulator_get(&pdev->dev, "qcom,alt-vdd");
158 if (IS_ERR(drvdata->alt_gdsc))
159 drvdata->alt_gdsc = NULL;
160
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700161 drvdata->pclk = devm_clk_get(&pdev->dev, "iface_clk");
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800162 if (IS_ERR(drvdata->pclk))
163 return PTR_ERR(drvdata->pclk);
164
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700165 drvdata->clk = devm_clk_get(&pdev->dev, "core_clk");
166 if (IS_ERR(drvdata->clk))
167 return PTR_ERR(drvdata->clk);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800168
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700169 needs_alt_core_clk = of_property_read_bool(pdev->dev.of_node,
170 "qcom,needs-alt-core-clk");
171 if (needs_alt_core_clk) {
172 drvdata->aclk = devm_clk_get(&pdev->dev, "alt_core_clk");
173 if (IS_ERR(drvdata->aclk))
174 return PTR_ERR(drvdata->aclk);
175 }
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800176
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700177 if (clk_get_rate(drvdata->clk) == 0) {
178 ret = clk_round_rate(drvdata->clk, 1);
179 clk_set_rate(drvdata->clk, ret);
180 }
181
182 if (drvdata->aclk && clk_get_rate(drvdata->aclk) == 0) {
183 ret = clk_round_rate(drvdata->aclk, 1);
184 clk_set_rate(drvdata->aclk, ret);
185 }
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800186
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800187 ret = msm_iommu_parse_dt(pdev, drvdata);
188 if (ret)
Stepan Moskovchenko17ae71e2012-07-24 19:24:14 -0700189 return ret;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800190
191 pr_info("device %s mapped at %p, with %d ctx banks\n",
192 drvdata->name, drvdata->base, drvdata->ncb);
193
194 platform_set_drvdata(pdev, drvdata);
195
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800196 return 0;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800197}
198
199static int __devexit msm_iommu_remove(struct platform_device *pdev)
200{
201 struct msm_iommu_drvdata *drv = NULL;
202
203 drv = platform_get_drvdata(pdev);
204 if (drv) {
Stephen Boyd55742b72012-08-08 11:40:26 -0700205 msm_iommu_remove_drv(drv);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800206 if (drv->clk)
207 clk_put(drv->clk);
208 clk_put(drv->pclk);
209 platform_set_drvdata(pdev, NULL);
210 }
211 return 0;
212}
213
214static int msm_iommu_ctx_parse_dt(struct platform_device *pdev,
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800215 struct msm_iommu_ctx_drvdata *ctx_drvdata)
216{
217 struct resource *r, rp;
Sathish Ambleycf045e62012-06-07 12:56:50 -0700218 int irq, ret;
Stepan Moskovchenko4575bdd2012-06-28 14:59:00 -0700219 u32 nsid;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800220
221 irq = platform_get_irq(pdev, 0);
222 if (irq > 0) {
223 ret = request_threaded_irq(irq, NULL,
224 msm_iommu_fault_handler_v2,
225 IRQF_ONESHOT | IRQF_SHARED,
226 "msm_iommu_nonsecure_irq", pdev);
227 if (ret) {
228 pr_err("Request IRQ %d failed with ret=%d\n", irq, ret);
229 return ret;
230 }
231 }
232
233 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
234 if (!r)
235 return -EINVAL;
236
237 ret = of_address_to_resource(pdev->dev.parent->of_node, 0, &rp);
238 if (ret)
239 return -EINVAL;
240
241 /* Calculate the context bank number using the base addresses. The
242 * first 8 pages belong to the global address space which is followed
243 * by the context banks, hence subtract by 8 to get the context bank
244 * number.
245 */
246 ctx_drvdata->num = ((r->start - rp.start) >> CTX_SHIFT) - 8;
247
Stepan Moskovchenkoce78fc22012-07-11 17:20:27 -0700248 if (of_property_read_string(pdev->dev.of_node, "label",
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800249 &ctx_drvdata->name))
250 ctx_drvdata->name = dev_name(&pdev->dev);
251
Stepan Moskovchenko4575bdd2012-06-28 14:59:00 -0700252 if (!of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-sids", &nsid))
253 return -EINVAL;
254
255 if (nsid >= sizeof(ctx_drvdata->sids))
256 return -EINVAL;
257
258 if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-sids",
259 ctx_drvdata->sids,
260 nsid / sizeof(*ctx_drvdata->sids))) {
261 return -EINVAL;
262 }
263 ctx_drvdata->nsid = nsid;
264
Olav Haugan26ddd432012-12-07 11:39:21 -0800265 ctx_drvdata->secure_context = of_property_read_bool(pdev->dev.of_node,
266 "qcom,secure-context");
267 ctx_drvdata->asid = -1;
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800268 return 0;
269}
270
271static int __devinit msm_iommu_ctx_probe(struct platform_device *pdev)
272{
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800273 struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL;
274 int ret;
275
276 if (!pdev->dev.parent)
277 return -EINVAL;
278
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800279 ctx_drvdata = devm_kzalloc(&pdev->dev, sizeof(*ctx_drvdata),
280 GFP_KERNEL);
281 if (!ctx_drvdata)
282 return -ENOMEM;
283
284 ctx_drvdata->pdev = pdev;
285 INIT_LIST_HEAD(&ctx_drvdata->attached_elm);
286 platform_set_drvdata(pdev, ctx_drvdata);
287
Sathish Ambleycf045e62012-06-07 12:56:50 -0700288 ret = msm_iommu_ctx_parse_dt(pdev, ctx_drvdata);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800289 if (!ret)
290 dev_info(&pdev->dev, "context %s using bank %d\n",
Stepan Moskovchenkoe14ca5c2012-07-11 12:40:51 -0700291 ctx_drvdata->name, ctx_drvdata->num);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800292
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800293 return ret;
294}
295
296static int __devexit msm_iommu_ctx_remove(struct platform_device *pdev)
297{
298 platform_set_drvdata(pdev, NULL);
299 return 0;
300}
301
302static struct of_device_id msm_iommu_match_table[] = {
303 { .compatible = "qcom,msm-smmu-v2", },
304 {}
305};
306
307static struct platform_driver msm_iommu_driver = {
308 .driver = {
309 .name = "msm_iommu_v2",
310 .of_match_table = msm_iommu_match_table,
311 },
312 .probe = msm_iommu_probe,
313 .remove = __devexit_p(msm_iommu_remove),
314};
315
316static struct of_device_id msm_iommu_ctx_match_table[] = {
317 { .name = "qcom,iommu-ctx", },
318 {}
319};
320
321static struct platform_driver msm_iommu_ctx_driver = {
322 .driver = {
323 .name = "msm_iommu_ctx_v2",
324 .of_match_table = msm_iommu_ctx_match_table,
325 },
326 .probe = msm_iommu_ctx_probe,
327 .remove = __devexit_p(msm_iommu_ctx_remove),
328};
329
330static int __init msm_iommu_driver_init(void)
331{
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800332 int ret;
333
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800334 ret = platform_driver_register(&msm_iommu_driver);
335 if (ret != 0) {
336 pr_err("Failed to register IOMMU driver\n");
337 goto error;
338 }
339
340 ret = platform_driver_register(&msm_iommu_ctx_driver);
341 if (ret != 0) {
342 pr_err("Failed to register IOMMU context driver\n");
343 goto error;
344 }
345
346error:
347 return ret;
348}
349
350static void __exit msm_iommu_driver_exit(void)
351{
352 platform_driver_unregister(&msm_iommu_ctx_driver);
353 platform_driver_unregister(&msm_iommu_driver);
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -0800354}
355
356subsys_initcall(msm_iommu_driver_init);
357module_exit(msm_iommu_driver_exit);
358
359MODULE_LICENSE("GPL v2");