blob: 549800f62aa8876174fbef80b11866491b6c15a5 [file] [log] [blame]
Olav Haugane6d01ef2013-01-25 16:55:44 -08001/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -07002 *
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.
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070011 */
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>
Stephen Boyd55742b72012-08-08 11:40:26 -070024#include <linux/list.h>
25#include <linux/mutex.h>
Olav Haugan95d24162012-12-05 14:47:47 -080026#include <linux/of.h>
27#include <linux/of_address.h>
28#include <linux/of_device.h>
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070029
Olav Hauganc5993142013-02-04 13:59:39 -080030#include <mach/iommu_perfmon.h>
Olav Haugane6d01ef2013-01-25 16:55:44 -080031#include <mach/iommu_hw-v0.h>
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070032#include <mach/iommu.h>
33
Stephen Boyd55742b72012-08-08 11:40:26 -070034static DEFINE_MUTEX(iommu_list_lock);
35static LIST_HEAD(iommu_list);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070036
Stephen Boyd55742b72012-08-08 11:40:26 -070037void msm_iommu_add_drv(struct msm_iommu_drvdata *drv)
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070038{
Stephen Boyd55742b72012-08-08 11:40:26 -070039 mutex_lock(&iommu_list_lock);
40 list_add(&drv->list, &iommu_list);
41 mutex_unlock(&iommu_list_lock);
42}
43
44void msm_iommu_remove_drv(struct msm_iommu_drvdata *drv)
45{
46 mutex_lock(&iommu_list_lock);
47 list_del(&drv->list);
48 mutex_unlock(&iommu_list_lock);
49}
50
51static int find_iommu_ctx(struct device *dev, void *data)
52{
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -080053 struct msm_iommu_ctx_drvdata *c;
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070054
Sathish Ambleyd1b89ed2012-02-07 21:47:47 -080055 c = dev_get_drvdata(dev);
Stephen Boyd55742b72012-08-08 11:40:26 -070056 if (!c || !c->name)
57 return 0;
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070058
Stephen Boyd55742b72012-08-08 11:40:26 -070059 return !strcmp(data, c->name);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070060}
61
Stephen Boyd55742b72012-08-08 11:40:26 -070062static struct device *find_context(struct device *dev, const char *name)
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070063{
Stephen Boyd55742b72012-08-08 11:40:26 -070064 return device_find_child(dev, (void *)name, find_iommu_ctx);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070065}
66
67struct device *msm_iommu_get_ctx(const char *ctx_name)
68{
Stephen Boyd55742b72012-08-08 11:40:26 -070069 struct msm_iommu_drvdata *drv;
70 struct device *dev = NULL;
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070071
Stephen Boyd55742b72012-08-08 11:40:26 -070072 mutex_lock(&iommu_list_lock);
73 list_for_each_entry(drv, &iommu_list, list) {
74 dev = find_context(drv->dev, ctx_name);
75 if (dev)
76 break;
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070077 }
Stephen Boyd55742b72012-08-08 11:40:26 -070078 mutex_unlock(&iommu_list_lock);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070079
Stephen Boyd55742b72012-08-08 11:40:26 -070080 if (!dev || !dev_get_drvdata(dev))
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070081 pr_err("Could not find context <%s>\n", ctx_name);
Stephen Boyd55742b72012-08-08 11:40:26 -070082 put_device(dev);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070083
Stephen Boyd55742b72012-08-08 11:40:26 -070084 return dev;
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070085}
86EXPORT_SYMBOL(msm_iommu_get_ctx);
87
Olav Haugan95d24162012-12-05 14:47:47 -080088static void msm_iommu_reset(void __iomem *base, void __iomem *glb_base, int ncb)
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070089{
Stepan Moskovchenkoa43d8c12011-02-24 18:00:42 -080090 int ctx;
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -070091
Olav Haugan95d24162012-12-05 14:47:47 -080092 SET_RPUE(glb_base, 0);
93 SET_RPUEIE(glb_base, 0);
94 SET_ESRRESTORE(glb_base, 0);
95 SET_TBE(glb_base, 0);
96 SET_CR(glb_base, 0);
97 SET_SPDMBE(glb_base, 0);
98 SET_TESTBUSCR(glb_base, 0);
99 SET_TLBRSW(glb_base, 0);
100 SET_GLOBAL_TLBIALL(glb_base, 0);
101 SET_RPU_ACR(glb_base, 0);
102 SET_TLBLKCRWE(glb_base, 1);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700103
104 for (ctx = 0; ctx < ncb; ctx++) {
Olav Haugan95d24162012-12-05 14:47:47 -0800105 SET_BPRCOSH(glb_base, ctx, 0);
106 SET_BPRCISH(glb_base, ctx, 0);
107 SET_BPRCNSH(glb_base, ctx, 0);
108 SET_BPSHCFG(glb_base, ctx, 0);
109 SET_BPMTCFG(glb_base, ctx, 0);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700110 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);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700119 SET_TLBFLPTER(base, ctx, 0);
120 SET_TLBSLPTER(base, ctx, 0);
121 SET_TLBLKCR(base, ctx, 0);
Stepan Moskovchenko0272fd62011-09-26 15:06:40 -0700122 SET_CTX_TLBIALL(base, ctx, 0);
123 SET_TLBIVA(base, ctx, 0);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700124 SET_PRRR(base, ctx, 0);
125 SET_NMRR(base, ctx, 0);
126 SET_CONTEXTIDR(base, ctx, 0);
127 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700128 mb();
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700129}
130
Olav Haugan95d24162012-12-05 14:47:47 -0800131static int msm_iommu_parse_dt(struct platform_device *pdev,
132 struct msm_iommu_drvdata *drvdata)
133{
134#ifdef CONFIG_OF_DEVICE
135 struct device_node *child;
136 struct resource *r;
137 u32 glb_offset = 0;
Olav Haugan7a2f99c2013-02-04 14:43:26 -0800138 int ret;
Olav Haugan95d24162012-12-05 14:47:47 -0800139
140 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
141 if (!r) {
142 pr_err("%s: Missing property reg\n", __func__);
143 return -EINVAL;
144 }
145 drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
146 if (!drvdata->base) {
Stepan Moskovchenko61fa3db2012-08-10 17:51:18 +0100147 pr_err("%s: Unable to ioremap %pr\n", __func__, r);
Olav Haugan95d24162012-12-05 14:47:47 -0800148 return -ENOMEM;
149 }
150 drvdata->glb_base = drvdata->base;
151
152 if (!of_property_read_u32(pdev->dev.of_node, "qcom,glb-offset",
153 &glb_offset)) {
154 drvdata->glb_base += glb_offset;
155 } else {
156 pr_err("%s: Missing property qcom,glb-offset\n", __func__);
157 return -EINVAL;
158 }
159
160 for_each_child_of_node(pdev->dev.of_node, child) {
161 drvdata->ncb++;
162 if (!of_platform_device_create(child, NULL, &pdev->dev))
163 pr_err("Failed to create %s device\n", child->name);
164 }
165
Olav Haugan7a2f99c2013-02-04 14:43:26 -0800166 ret = of_property_read_string(pdev->dev.of_node, "label",
167 &drvdata->name);
168 if (ret) {
169 pr_err("%s: Missing property label\n", __func__);
170 return -EINVAL;
171 }
Olav Haugan95d24162012-12-05 14:47:47 -0800172 drvdata->sec_id = -1;
173 drvdata->ttbr_split = 0;
174#endif
175 return 0;
176}
177
178static int __get_clocks(struct platform_device *pdev,
179 struct msm_iommu_drvdata *drvdata)
180{
181 int ret = 0;
182
183 drvdata->pclk = clk_get(&pdev->dev, "iface_clk");
184 if (IS_ERR(drvdata->pclk)) {
185 ret = PTR_ERR(drvdata->pclk);
186 drvdata->pclk = NULL;
187 pr_err("Unable to get %s clock for %s IOMMU device\n",
188 dev_name(&pdev->dev), drvdata->name);
189 goto fail;
190 }
191
192 drvdata->clk = clk_get(&pdev->dev, "core_clk");
193
194 if (!IS_ERR(drvdata->clk)) {
195 if (clk_get_rate(drvdata->clk) == 0) {
196 ret = clk_round_rate(drvdata->clk, 1000);
197 clk_set_rate(drvdata->clk, ret);
198 }
199 } else {
200 drvdata->clk = NULL;
201 }
202 return 0;
203fail:
204 return ret;
205}
206
207static void __put_clocks(struct msm_iommu_drvdata *drvdata)
208{
209 if (drvdata->clk)
210 clk_put(drvdata->clk);
211 clk_put(drvdata->pclk);
212}
213
214static int __enable_clocks(struct msm_iommu_drvdata *drvdata)
215{
216 int ret;
217
218 ret = clk_prepare_enable(drvdata->pclk);
219 if (ret)
220 goto fail;
221
222 if (drvdata->clk) {
223 ret = clk_prepare_enable(drvdata->clk);
224 if (ret)
225 clk_disable_unprepare(drvdata->pclk);
226 }
227fail:
228 return ret;
229}
230
231static void __disable_clocks(struct msm_iommu_drvdata *drvdata)
232{
233 if (drvdata->clk)
234 clk_disable_unprepare(drvdata->clk);
235 clk_disable_unprepare(drvdata->pclk);
236}
237
238/*
239 * Do a basic check of the IOMMU by performing an ATS operation
240 * on context bank 0.
241 */
242static int iommu_sanity_check(struct msm_iommu_drvdata *drvdata)
243{
244 int par;
245 int ret = 0;
246
247 SET_M(drvdata->base, 0, 1);
248 SET_PAR(drvdata->base, 0, 0);
249 SET_V2PCFG(drvdata->base, 0, 1);
250 SET_V2PPR(drvdata->base, 0, 0);
251 mb();
252 par = GET_PAR(drvdata->base, 0);
253 SET_V2PCFG(drvdata->base, 0, 0);
254 SET_M(drvdata->base, 0, 0);
255 mb();
256
257 if (!par) {
258 pr_err("%s: Invalid PAR value detected\n", drvdata->name);
259 ret = -ENODEV;
260 }
261 return ret;
262}
263
Olav Hauganc5993142013-02-04 13:59:39 -0800264static int msm_iommu_pmon_parse_dt(struct platform_device *pdev,
265 struct iommu_pmon *pmon_info)
266{
267 int ret = 0;
268 int irq = platform_get_irq(pdev, 0);
269 unsigned int cls_prop_size;
270
271 if (irq > 0) {
272 pmon_info->iommu.evt_irq = platform_get_irq(pdev, 0);
273
274 ret = of_property_read_u32(pdev->dev.of_node,
275 "qcom,iommu-pmu-ngroups",
276 &pmon_info->num_groups);
277 if (ret) {
278 pr_err("Error reading qcom,iommu-pmu-ngroups\n");
279 goto fail;
280 }
281 ret = of_property_read_u32(pdev->dev.of_node,
282 "qcom,iommu-pmu-ncounters",
283 &pmon_info->num_counters);
284 if (ret) {
285 pr_err("Error reading qcom,iommu-pmu-ncounters\n");
286 goto fail;
287 }
288
289 if (!of_get_property(pdev->dev.of_node,
290 "qcom,iommu-pmu-event-classes",
291 &cls_prop_size)) {
292 pr_err("Error reading qcom,iommu-pmu-event-classes\n");
293 return -EINVAL;
294 }
295
296 pmon_info->event_cls_supported =
297 devm_kzalloc(&pdev->dev, cls_prop_size, GFP_KERNEL);
298
299 if (!pmon_info->event_cls_supported) {
300 pr_err("Unable to get memory for event class array\n");
301 return -ENOMEM;
302 }
303
304 pmon_info->nevent_cls_supported = cls_prop_size / sizeof(u32);
305
306 ret = of_property_read_u32_array(pdev->dev.of_node,
307 "qcom,iommu-pmu-event-classes",
308 pmon_info->event_cls_supported,
309 pmon_info->nevent_cls_supported);
310 if (ret) {
311 pr_err("Error reading qcom,iommu-pmu-event-classes\n");
312 return ret;
313 }
314 } else {
315 pmon_info->iommu.evt_irq = -1;
316 ret = irq;
317 }
318
319fail:
320 return ret;
321}
322
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700323static int msm_iommu_probe(struct platform_device *pdev)
324{
Olav Hauganc5993142013-02-04 13:59:39 -0800325 struct iommu_pmon *pmon_info;
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700326 struct msm_iommu_drvdata *drvdata;
327 struct msm_iommu_dev *iommu_dev = pdev->dev.platform_data;
Olav Haugan95d24162012-12-05 14:47:47 -0800328 int ret;
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700329
Olav Haugan95d24162012-12-05 14:47:47 -0800330 drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700331
Stepan Moskovchenkob61401a2011-02-28 16:03:02 -0800332 if (!drvdata) {
333 ret = -ENOMEM;
334 goto fail;
335 }
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700336
Olav Haugan95d24162012-12-05 14:47:47 -0800337 if (pdev->dev.of_node) {
338 ret = msm_iommu_parse_dt(pdev, drvdata);
339 if (ret)
340 goto fail;
341 } else if (pdev->dev.platform_data) {
342 struct resource *r, *r2;
343 resource_size_t len;
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700344
Olav Haugan95d24162012-12-05 14:47:47 -0800345 r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
346 "physbase");
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700347
Olav Haugan95d24162012-12-05 14:47:47 -0800348 if (!r) {
349 ret = -ENODEV;
350 goto fail;
Matt Wagantall99858222011-11-08 14:31:27 -0800351 }
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700352
Olav Haugan95d24162012-12-05 14:47:47 -0800353 len = resource_size(r);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700354
Olav Haugan95d24162012-12-05 14:47:47 -0800355 r2 = request_mem_region(r->start, len, r->name);
356 if (!r2) {
Stepan Moskovchenko61fa3db2012-08-10 17:51:18 +0100357 pr_err("Could not request memory region: %pr\n", r);
Olav Haugan95d24162012-12-05 14:47:47 -0800358 ret = -EBUSY;
359 goto fail;
360 }
Stepan Moskovchenkob61401a2011-02-28 16:03:02 -0800361
Olav Haugan95d24162012-12-05 14:47:47 -0800362 drvdata->base = devm_ioremap(&pdev->dev, r2->start, len);
Stepan Moskovchenkob61401a2011-02-28 16:03:02 -0800363
Olav Haugan95d24162012-12-05 14:47:47 -0800364 if (!drvdata->base) {
Stepan Moskovchenko61fa3db2012-08-10 17:51:18 +0100365 pr_err("Could not ioremap: %pr\n", r);
Olav Haugan95d24162012-12-05 14:47:47 -0800366 ret = -EBUSY;
367 goto fail;
368 }
369 /*
370 * Global register space offset for legacy IOMMUv1 hardware
371 * is always 0xFF000
372 */
373 drvdata->glb_base = drvdata->base + 0xFF000;
374 drvdata->name = iommu_dev->name;
375 drvdata->dev = &pdev->dev;
376 drvdata->ncb = iommu_dev->ncb;
377 drvdata->ttbr_split = iommu_dev->ttbr_split;
378 } else {
Stepan Moskovchenkob61401a2011-02-28 16:03:02 -0800379 ret = -ENODEV;
Olav Haugan95d24162012-12-05 14:47:47 -0800380 goto fail;
Stepan Moskovchenkob61401a2011-02-28 16:03:02 -0800381 }
382
Stephen Boyd55742b72012-08-08 11:40:26 -0700383 drvdata->dev = &pdev->dev;
384
Olav Haugan95d24162012-12-05 14:47:47 -0800385 ret = __get_clocks(pdev, drvdata);
386
387 if (ret)
388 goto fail;
389
390 __enable_clocks(drvdata);
391
392 msm_iommu_reset(drvdata->base, drvdata->glb_base, drvdata->ncb);
393
394 ret = iommu_sanity_check(drvdata);
395 if (ret)
396 goto fail_clk;
Stepan Moskovchenkob61401a2011-02-28 16:03:02 -0800397
Stepan Moskovchenko73a50f62012-05-03 17:29:12 -0700398 pr_info("device %s mapped at %p, with %d ctx banks\n",
Olav Haugan95d24162012-12-05 14:47:47 -0800399 drvdata->name, drvdata->base, drvdata->ncb);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700400
Olav Haugan95d24162012-12-05 14:47:47 -0800401 msm_iommu_add_drv(drvdata);
Stepan Moskovchenkob61401a2011-02-28 16:03:02 -0800402 platform_set_drvdata(pdev, drvdata);
403
Olav Haugan95d24162012-12-05 14:47:47 -0800404 __disable_clocks(drvdata);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700405
Olav Hauganc5993142013-02-04 13:59:39 -0800406 pmon_info = msm_iommu_pm_alloc(&pdev->dev);
407 if (pmon_info != NULL) {
408 ret = msm_iommu_pmon_parse_dt(pdev, pmon_info);
409 if (ret) {
410 msm_iommu_pm_free(&pdev->dev);
411 pr_info("%s: pmon not available.\n", drvdata->name);
412 } else {
413 pmon_info->iommu.base = drvdata->base;
414 pmon_info->iommu.ops = &iommu_access_ops_v0;
415 pmon_info->iommu.hw_ops = iommu_pm_get_hw_ops_v0();
416 pmon_info->iommu.iommu_name = drvdata->name;
417 ret = msm_iommu_pm_iommu_register(pmon_info);
418 if (ret) {
419 pr_err("%s iommu register fail\n",
420 drvdata->name);
421 msm_iommu_pm_free(&pdev->dev);
422 } else {
423 pr_debug("%s iommu registered for pmon\n",
424 pmon_info->iommu.iommu_name);
425 }
426 }
427 }
428
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700429 return 0;
Olav Haugan95d24162012-12-05 14:47:47 -0800430
Stepan Moskovchenkob61401a2011-02-28 16:03:02 -0800431fail_clk:
Olav Haugan95d24162012-12-05 14:47:47 -0800432 __disable_clocks(drvdata);
433 __put_clocks(drvdata);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700434fail:
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700435 return ret;
436}
437
438static int msm_iommu_remove(struct platform_device *pdev)
439{
440 struct msm_iommu_drvdata *drv = NULL;
441
442 drv = platform_get_drvdata(pdev);
443 if (drv) {
Stephen Boyd55742b72012-08-08 11:40:26 -0700444 msm_iommu_remove_drv(drv);
Stepan Moskovchenkob61401a2011-02-28 16:03:02 -0800445 if (drv->clk)
446 clk_put(drv->clk);
447 clk_put(drv->pclk);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700448 platform_set_drvdata(pdev, NULL);
449 }
450 return 0;
451}
452
Olav Haugan95d24162012-12-05 14:47:47 -0800453static int msm_iommu_ctx_parse_dt(struct platform_device *pdev,
454 struct msm_iommu_ctx_drvdata *ctx_drvdata)
455{
456 struct resource *r, rp;
457 int irq, ret;
458 u32 nmid_array_size;
459 u32 nmid;
460
461 irq = platform_get_irq(pdev, 0);
462 if (irq > 0) {
463 ret = request_threaded_irq(irq, NULL,
464 msm_iommu_fault_handler,
465 IRQF_ONESHOT | IRQF_SHARED,
Olav Haugan3b9d8542013-02-28 17:49:00 -0800466 "msm_iommu_nonsecure_irq", ctx_drvdata);
Olav Haugan95d24162012-12-05 14:47:47 -0800467 if (ret) {
468 pr_err("Request IRQ %d failed with ret=%d\n", irq, ret);
469 return ret;
470 }
471 }
472
473 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
474 if (!r) {
475 pr_err("Could not find reg property for context bank\n");
476 return -EINVAL;
477 }
478
479 ret = of_address_to_resource(pdev->dev.parent->of_node, 0, &rp);
480 if (ret) {
481 pr_err("of_address_to_resource failed\n");
482 return -EINVAL;
483 }
484
485 /* Calculate the context bank number using the base addresses. CB0
486 * starts at the base address.
487 */
488 ctx_drvdata->num = ((r->start - rp.start) >> CTX_SHIFT);
489
490 if (of_property_read_string(pdev->dev.of_node, "label",
491 &ctx_drvdata->name)) {
492 pr_err("Could not find label property\n");
493 return -EINVAL;
494 }
495
496 if (!of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-mids",
497 &nmid_array_size)) {
498 pr_err("Could not find iommu-ctx-mids property\n");
499 return -EINVAL;
500 }
501 if (nmid_array_size >= sizeof(ctx_drvdata->sids)) {
502 pr_err("Too many mids defined - array size: %u, mids size: %u\n",
503 nmid_array_size, sizeof(ctx_drvdata->sids));
504 return -EINVAL;
505 }
506 nmid = nmid_array_size / sizeof(*ctx_drvdata->sids);
507
508 if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-mids",
509 ctx_drvdata->sids, nmid)) {
510 pr_err("Could not find iommu-ctx-mids property\n");
511 return -EINVAL;
512 }
513 ctx_drvdata->nsid = nmid;
514
515 return 0;
516}
517
518static void __program_m2v_tables(struct msm_iommu_drvdata *drvdata,
519 struct msm_iommu_ctx_drvdata *ctx_drvdata)
520{
521 int i;
522
523 /* Program the M2V tables for this context */
524 for (i = 0; i < ctx_drvdata->nsid; i++) {
525 int sid = ctx_drvdata->sids[i];
526 int num = ctx_drvdata->num;
527
528 SET_M2VCBR_N(drvdata->glb_base, sid, 0);
529 SET_CBACR_N(drvdata->glb_base, num, 0);
530
531 /* Route page faults to the non-secure interrupt */
532 SET_IRPTNDX(drvdata->glb_base, num, 1);
533
534 /* Set VMID = 0 */
535 SET_VMID(drvdata->glb_base, sid, 0);
536
537 /* Set the context number for that SID to this context */
538 SET_CBNDX(drvdata->glb_base, sid, num);
539
540 /* Set SID associated with this context bank to 0 */
541 SET_CBVMID(drvdata->glb_base, num, 0);
542
543 /* Set the ASID for TLB tagging for this context to 0 */
544 SET_CONTEXTIDR_ASID(drvdata->base, num, 0);
545
546 /* Set security bit override to be Non-secure */
547 SET_NSCFG(drvdata->glb_base, sid, 3);
548 }
549 mb();
550}
551
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700552static int msm_iommu_ctx_probe(struct platform_device *pdev)
553{
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700554 struct msm_iommu_drvdata *drvdata;
555 struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL;
Stepan Moskovchenko73a50f62012-05-03 17:29:12 -0700556 int i, ret, irq;
Olav Haugan95d24162012-12-05 14:47:47 -0800557 if (!pdev->dev.parent) {
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700558 ret = -EINVAL;
559 goto fail;
560 }
561
562 drvdata = dev_get_drvdata(pdev->dev.parent);
563
564 if (!drvdata) {
565 ret = -ENODEV;
566 goto fail;
567 }
568
Olav Haugan95d24162012-12-05 14:47:47 -0800569 ctx_drvdata = devm_kzalloc(&pdev->dev, sizeof(*ctx_drvdata),
570 GFP_KERNEL);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700571 if (!ctx_drvdata) {
572 ret = -ENOMEM;
573 goto fail;
574 }
Stepan Moskovchenko73a50f62012-05-03 17:29:12 -0700575
Olav Haugan95d24162012-12-05 14:47:47 -0800576 ctx_drvdata->pdev = pdev;
577 INIT_LIST_HEAD(&ctx_drvdata->attached_elm);
578 platform_set_drvdata(pdev, ctx_drvdata);
Olav Haugane99ee7e2012-12-11 15:02:02 -0800579 ctx_drvdata->attach_count = 0;
Olav Haugan95d24162012-12-05 14:47:47 -0800580
581 if (pdev->dev.of_node) {
582 ret = msm_iommu_ctx_parse_dt(pdev, ctx_drvdata);
583 if (ret)
584 goto fail;
585 } else if (pdev->dev.platform_data) {
586 struct msm_iommu_ctx_dev *c = pdev->dev.platform_data;
587
588 ctx_drvdata->num = c->num;
589 ctx_drvdata->name = c->name;
590
591 for (i = 0; i < MAX_NUM_MIDS; ++i) {
592 if (c->mids[i] == -1) {
593 ctx_drvdata->nsid = i;
594 break;
595 }
596 ctx_drvdata->sids[i] = c->mids[i];
597 }
598 irq = platform_get_irq_byname(
599 to_platform_device(pdev->dev.parent),
600 "nonsecure_irq");
601 if (irq < 0) {
602 ret = -ENODEV;
603 goto fail;
604 }
605
606 ret = request_threaded_irq(irq, NULL, msm_iommu_fault_handler,
607 IRQF_ONESHOT | IRQF_SHARED,
608 "msm_iommu_nonsecure_irq", ctx_drvdata);
609
610 if (ret) {
611 pr_err("request_threaded_irq %d failed: %d\n", irq,
612 ret);
613 goto fail;
614 }
615 } else {
Stepan Moskovchenko73a50f62012-05-03 17:29:12 -0700616 ret = -ENODEV;
617 goto fail;
618 }
619
Olav Haugan95d24162012-12-05 14:47:47 -0800620 __enable_clocks(drvdata);
621 __program_m2v_tables(drvdata, ctx_drvdata);
622 __disable_clocks(drvdata);
Stepan Moskovchenko73a50f62012-05-03 17:29:12 -0700623
Olav Haugan95d24162012-12-05 14:47:47 -0800624 dev_info(&pdev->dev, "context %s using bank %d\n", ctx_drvdata->name,
625 ctx_drvdata->num);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700626 return 0;
627fail:
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700628 return ret;
629}
630
Olav Haugan95d24162012-12-05 14:47:47 -0800631static int __devexit msm_iommu_ctx_remove(struct platform_device *pdev)
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700632{
Olav Haugan95d24162012-12-05 14:47:47 -0800633 platform_set_drvdata(pdev, NULL);
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700634 return 0;
635}
636
Olav Haugan95d24162012-12-05 14:47:47 -0800637
638static struct of_device_id msm_iommu_match_table[] = {
Olav Haugan0e22c482013-01-28 17:39:36 -0800639 { .compatible = "qcom,msm-smmu-v0", },
Olav Haugan95d24162012-12-05 14:47:47 -0800640 {}
641};
642
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700643static struct platform_driver msm_iommu_driver = {
644 .driver = {
Olav Haugan0e22c482013-01-28 17:39:36 -0800645 .name = "msm_iommu-v0",
Olav Haugan95d24162012-12-05 14:47:47 -0800646 .of_match_table = msm_iommu_match_table,
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700647 },
648 .probe = msm_iommu_probe,
Olav Haugan95d24162012-12-05 14:47:47 -0800649 .remove = __devexit_p(msm_iommu_remove),
650};
651
652static struct of_device_id msm_iommu_ctx_match_table[] = {
653 { .name = "qcom,iommu-ctx", },
654 {}
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700655};
656
657static struct platform_driver msm_iommu_ctx_driver = {
658 .driver = {
659 .name = "msm_iommu_ctx",
Olav Haugan95d24162012-12-05 14:47:47 -0800660 .of_match_table = msm_iommu_ctx_match_table,
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700661 },
662 .probe = msm_iommu_ctx_probe,
Olav Haugan95d24162012-12-05 14:47:47 -0800663 .remove = __devexit_p(msm_iommu_ctx_remove),
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700664};
665
Stepan Moskovchenko516cbc72010-11-12 19:29:53 -0800666static int __init msm_iommu_driver_init(void)
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700667{
668 int ret;
669 ret = platform_driver_register(&msm_iommu_driver);
670 if (ret != 0) {
671 pr_err("Failed to register IOMMU driver\n");
672 goto error;
673 }
674
675 ret = platform_driver_register(&msm_iommu_ctx_driver);
676 if (ret != 0) {
677 pr_err("Failed to register IOMMU context driver\n");
678 goto error;
679 }
680
681error:
682 return ret;
683}
684
Stepan Moskovchenko516cbc72010-11-12 19:29:53 -0800685static void __exit msm_iommu_driver_exit(void)
Stepan Moskovchenkoc6a59512010-08-24 18:32:38 -0700686{
687 platform_driver_unregister(&msm_iommu_ctx_driver);
688 platform_driver_unregister(&msm_iommu_driver);
689}
690
691subsys_initcall(msm_iommu_driver_init);
692module_exit(msm_iommu_driver_exit);
693
694MODULE_LICENSE("GPL v2");
695MODULE_AUTHOR("Stepan Moskovchenko <stepanm@codeaurora.org>");