blob: 08cf7ec5b4a5f5b00bb65fa665cffe6f4db4c673 [file] [log] [blame]
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +02001/*
2 * omap iommu: tlb and pagetable primitives
3 *
Hiroshi DOYUc127c7d2010-02-15 10:03:32 -08004 * Copyright (C) 2008-2010 Nokia Corporation
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +02005 *
6 * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>,
7 * Paul Mundt and Toshihiro Kobayashi
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/err.h>
15#include <linux/module.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090016#include <linux/slab.h>
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +020017#include <linux/interrupt.h>
18#include <linux/ioport.h>
19#include <linux/clk.h>
20#include <linux/platform_device.h>
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +030021#include <linux/iommu.h>
22#include <linux/mutex.h>
23#include <linux/spinlock.h>
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +020024
25#include <asm/cacheflush.h>
26
Tony Lindgrence491cf2009-10-20 09:40:47 -070027#include <plat/iommu.h>
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +020028
Ohad Ben-Cohenfcf3a6e2011-08-15 23:21:41 +030029#include <plat/iopgtable.h>
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +020030
Hiroshi DOYU37c28362010-04-27 05:37:12 +000031#define for_each_iotlb_cr(obj, n, __i, cr) \
32 for (__i = 0; \
33 (__i < (n)) && (cr = __iotlb_read_cr((obj), __i), true); \
34 __i++)
35
Ohad Ben-Cohen66bc8cf2011-11-10 11:32:27 +020036/* bitmap of the page sizes currently supported */
37#define OMAP_IOMMU_PGSIZES (SZ_4K | SZ_64K | SZ_1M | SZ_16M)
38
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +030039/**
40 * struct omap_iommu_domain - omap iommu domain
41 * @pgtable: the page table
42 * @iommu_dev: an omap iommu device attached to this domain. only a single
43 * iommu device can be attached for now.
44 * @lock: domain lock, should be taken when attaching/detaching
45 */
46struct omap_iommu_domain {
47 u32 *pgtable;
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +030048 struct omap_iommu *iommu_dev;
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +030049 spinlock_t lock;
50};
51
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +020052/* accommodate the difference between omap1 and omap2/3 */
53static const struct iommu_functions *arch_iommu;
54
55static struct platform_driver omap_iommu_driver;
56static struct kmem_cache *iopte_cachep;
57
58/**
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +030059 * omap_install_iommu_arch - Install archtecure specific iommu functions
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +020060 * @ops: a pointer to architecture specific iommu functions
61 *
62 * There are several kind of iommu algorithm(tlb, pagetable) among
63 * omap series. This interface installs such an iommu algorighm.
64 **/
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +030065int omap_install_iommu_arch(const struct iommu_functions *ops)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +020066{
67 if (arch_iommu)
68 return -EBUSY;
69
70 arch_iommu = ops;
71 return 0;
72}
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +030073EXPORT_SYMBOL_GPL(omap_install_iommu_arch);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +020074
75/**
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +030076 * omap_uninstall_iommu_arch - Uninstall archtecure specific iommu functions
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +020077 * @ops: a pointer to architecture specific iommu functions
78 *
79 * This interface uninstalls the iommu algorighm installed previously.
80 **/
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +030081void omap_uninstall_iommu_arch(const struct iommu_functions *ops)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +020082{
83 if (arch_iommu != ops)
84 pr_err("%s: not your arch\n", __func__);
85
86 arch_iommu = NULL;
87}
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +030088EXPORT_SYMBOL_GPL(omap_uninstall_iommu_arch);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +020089
90/**
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +030091 * omap_iommu_save_ctx - Save registers for pm off-mode support
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +020092 * @obj: target iommu
93 **/
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +030094void omap_iommu_save_ctx(struct omap_iommu *obj)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +020095{
96 arch_iommu->save_ctx(obj);
97}
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +030098EXPORT_SYMBOL_GPL(omap_iommu_save_ctx);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +020099
100/**
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300101 * omap_iommu_restore_ctx - Restore registers for pm off-mode support
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200102 * @obj: target iommu
103 **/
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300104void omap_iommu_restore_ctx(struct omap_iommu *obj)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200105{
106 arch_iommu->restore_ctx(obj);
107}
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300108EXPORT_SYMBOL_GPL(omap_iommu_restore_ctx);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200109
110/**
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300111 * omap_iommu_arch_version - Return running iommu arch version
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200112 **/
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300113u32 omap_iommu_arch_version(void)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200114{
115 return arch_iommu->version;
116}
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300117EXPORT_SYMBOL_GPL(omap_iommu_arch_version);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200118
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300119static int iommu_enable(struct omap_iommu *obj)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200120{
121 int err;
122
123 if (!obj)
124 return -EINVAL;
125
Martin Hostettleref4815a2011-02-24 12:51:31 -0800126 if (!arch_iommu)
127 return -ENODEV;
128
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200129 clk_enable(obj->clk);
130
131 err = arch_iommu->enable(obj);
132
133 clk_disable(obj->clk);
134 return err;
135}
136
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300137static void iommu_disable(struct omap_iommu *obj)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200138{
139 if (!obj)
140 return;
141
142 clk_enable(obj->clk);
143
144 arch_iommu->disable(obj);
145
146 clk_disable(obj->clk);
147}
148
149/*
150 * TLB operations
151 */
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300152void omap_iotlb_cr_to_e(struct cr_regs *cr, struct iotlb_entry *e)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200153{
154 BUG_ON(!cr || !e);
155
156 arch_iommu->cr_to_e(cr, e);
157}
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300158EXPORT_SYMBOL_GPL(omap_iotlb_cr_to_e);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200159
160static inline int iotlb_cr_valid(struct cr_regs *cr)
161{
162 if (!cr)
163 return -EINVAL;
164
165 return arch_iommu->cr_valid(cr);
166}
167
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300168static inline struct cr_regs *iotlb_alloc_cr(struct omap_iommu *obj,
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200169 struct iotlb_entry *e)
170{
171 if (!e)
172 return NULL;
173
174 return arch_iommu->alloc_cr(obj, e);
175}
176
Ohad Ben-Cohene1f23812011-08-16 14:58:14 +0300177static u32 iotlb_cr_to_virt(struct cr_regs *cr)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200178{
179 return arch_iommu->cr_to_virt(cr);
180}
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200181
182static u32 get_iopte_attr(struct iotlb_entry *e)
183{
184 return arch_iommu->get_pte_attr(e);
185}
186
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300187static u32 iommu_report_fault(struct omap_iommu *obj, u32 *da)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200188{
189 return arch_iommu->fault_isr(obj, da);
190}
191
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300192static void iotlb_lock_get(struct omap_iommu *obj, struct iotlb_lock *l)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200193{
194 u32 val;
195
196 val = iommu_read_reg(obj, MMU_LOCK);
197
198 l->base = MMU_LOCK_BASE(val);
199 l->vict = MMU_LOCK_VICT(val);
200
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200201}
202
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300203static void iotlb_lock_set(struct omap_iommu *obj, struct iotlb_lock *l)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200204{
205 u32 val;
206
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200207 val = (l->base << MMU_LOCK_BASE_SHIFT);
208 val |= (l->vict << MMU_LOCK_VICT_SHIFT);
209
210 iommu_write_reg(obj, val, MMU_LOCK);
211}
212
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300213static void iotlb_read_cr(struct omap_iommu *obj, struct cr_regs *cr)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200214{
215 arch_iommu->tlb_read_cr(obj, cr);
216}
217
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300218static void iotlb_load_cr(struct omap_iommu *obj, struct cr_regs *cr)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200219{
220 arch_iommu->tlb_load_cr(obj, cr);
221
222 iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY);
223 iommu_write_reg(obj, 1, MMU_LD_TLB);
224}
225
226/**
227 * iotlb_dump_cr - Dump an iommu tlb entry into buf
228 * @obj: target iommu
229 * @cr: contents of cam and ram register
230 * @buf: output buffer
231 **/
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300232static inline ssize_t iotlb_dump_cr(struct omap_iommu *obj, struct cr_regs *cr,
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200233 char *buf)
234{
235 BUG_ON(!cr || !buf);
236
237 return arch_iommu->dump_cr(obj, cr, buf);
238}
239
Hiroshi DOYU37c28362010-04-27 05:37:12 +0000240/* only used in iotlb iteration for-loop */
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300241static struct cr_regs __iotlb_read_cr(struct omap_iommu *obj, int n)
Hiroshi DOYU37c28362010-04-27 05:37:12 +0000242{
243 struct cr_regs cr;
244 struct iotlb_lock l;
245
246 iotlb_lock_get(obj, &l);
247 l.vict = n;
248 iotlb_lock_set(obj, &l);
249 iotlb_read_cr(obj, &cr);
250
251 return cr;
252}
253
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200254/**
255 * load_iotlb_entry - Set an iommu tlb entry
256 * @obj: target iommu
257 * @e: an iommu tlb entry info
258 **/
Ohad Ben-Cohen5da14a42011-08-16 15:19:10 +0300259#ifdef PREFETCH_IOTLB
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300260static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200261{
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200262 int err = 0;
263 struct iotlb_lock l;
264 struct cr_regs *cr;
265
266 if (!obj || !obj->nr_tlb_entries || !e)
267 return -EINVAL;
268
269 clk_enable(obj->clk);
270
Kanigeri, Haribe6d8022010-04-22 23:26:11 +0000271 iotlb_lock_get(obj, &l);
272 if (l.base == obj->nr_tlb_entries) {
273 dev_warn(obj->dev, "%s: preserve entries full\n", __func__);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200274 err = -EBUSY;
275 goto out;
276 }
Kanigeri, Haribe6d8022010-04-22 23:26:11 +0000277 if (!e->prsvd) {
Hiroshi DOYU37c28362010-04-27 05:37:12 +0000278 int i;
279 struct cr_regs tmp;
Kanigeri, Haribe6d8022010-04-22 23:26:11 +0000280
Hiroshi DOYU37c28362010-04-27 05:37:12 +0000281 for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, tmp)
Kanigeri, Haribe6d8022010-04-22 23:26:11 +0000282 if (!iotlb_cr_valid(&tmp))
283 break;
Hiroshi DOYU37c28362010-04-27 05:37:12 +0000284
Kanigeri, Haribe6d8022010-04-22 23:26:11 +0000285 if (i == obj->nr_tlb_entries) {
286 dev_dbg(obj->dev, "%s: full: no entry\n", __func__);
287 err = -EBUSY;
288 goto out;
289 }
Hiroshi DOYU37c28362010-04-27 05:37:12 +0000290
291 iotlb_lock_get(obj, &l);
Kanigeri, Haribe6d8022010-04-22 23:26:11 +0000292 } else {
293 l.vict = l.base;
294 iotlb_lock_set(obj, &l);
295 }
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200296
297 cr = iotlb_alloc_cr(obj, e);
298 if (IS_ERR(cr)) {
299 clk_disable(obj->clk);
300 return PTR_ERR(cr);
301 }
302
303 iotlb_load_cr(obj, cr);
304 kfree(cr);
305
Kanigeri, Haribe6d8022010-04-22 23:26:11 +0000306 if (e->prsvd)
307 l.base++;
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200308 /* increment victim for next tlb load */
309 if (++l.vict == obj->nr_tlb_entries)
Kanigeri, Haribe6d8022010-04-22 23:26:11 +0000310 l.vict = l.base;
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200311 iotlb_lock_set(obj, &l);
312out:
313 clk_disable(obj->clk);
314 return err;
315}
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200316
Ohad Ben-Cohen5da14a42011-08-16 15:19:10 +0300317#else /* !PREFETCH_IOTLB */
318
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300319static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
Ohad Ben-Cohen5da14a42011-08-16 15:19:10 +0300320{
321 return 0;
322}
323
324#endif /* !PREFETCH_IOTLB */
325
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300326static int prefetch_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
Ohad Ben-Cohen5da14a42011-08-16 15:19:10 +0300327{
328 return load_iotlb_entry(obj, e);
329}
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200330
331/**
332 * flush_iotlb_page - Clear an iommu tlb entry
333 * @obj: target iommu
334 * @da: iommu device virtual address
335 *
336 * Clear an iommu tlb entry which includes 'da' address.
337 **/
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300338static void flush_iotlb_page(struct omap_iommu *obj, u32 da)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200339{
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200340 int i;
Hiroshi DOYU37c28362010-04-27 05:37:12 +0000341 struct cr_regs cr;
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200342
343 clk_enable(obj->clk);
344
Hiroshi DOYU37c28362010-04-27 05:37:12 +0000345 for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr) {
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200346 u32 start;
347 size_t bytes;
348
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200349 if (!iotlb_cr_valid(&cr))
350 continue;
351
352 start = iotlb_cr_to_virt(&cr);
353 bytes = iopgsz_to_bytes(cr.cam & 3);
354
355 if ((start <= da) && (da < start + bytes)) {
356 dev_dbg(obj->dev, "%s: %08x<=%08x(%x)\n",
357 __func__, start, da, bytes);
Hari Kanigeri0fa035e2010-08-20 13:50:18 +0000358 iotlb_load_cr(obj, &cr);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200359 iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY);
360 }
361 }
362 clk_disable(obj->clk);
363
364 if (i == obj->nr_tlb_entries)
365 dev_dbg(obj->dev, "%s: no page for %08x\n", __func__, da);
366}
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200367
368/**
369 * flush_iotlb_all - Clear all iommu tlb entries
370 * @obj: target iommu
371 **/
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300372static void flush_iotlb_all(struct omap_iommu *obj)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200373{
374 struct iotlb_lock l;
375
376 clk_enable(obj->clk);
377
378 l.base = 0;
379 l.vict = 0;
380 iotlb_lock_set(obj, &l);
381
382 iommu_write_reg(obj, 1, MMU_GFLUSH);
383
384 clk_disable(obj->clk);
385}
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200386
Arnd Bergmanne4efd942011-10-02 14:34:05 -0400387#if defined(CONFIG_OMAP_IOMMU_DEBUG) || defined(CONFIG_OMAP_IOMMU_DEBUG_MODULE)
Kanigeri, Hariddfa9752010-05-24 02:01:51 +0000388
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300389ssize_t omap_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t bytes)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200390{
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200391 if (!obj || !buf)
392 return -EINVAL;
393
394 clk_enable(obj->clk);
395
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700396 bytes = arch_iommu->dump_ctx(obj, buf, bytes);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200397
398 clk_disable(obj->clk);
399
400 return bytes;
401}
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300402EXPORT_SYMBOL_GPL(omap_iommu_dump_ctx);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200403
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300404static int
405__dump_tlb_entries(struct omap_iommu *obj, struct cr_regs *crs, int num)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200406{
407 int i;
Hiroshi DOYU37c28362010-04-27 05:37:12 +0000408 struct iotlb_lock saved;
409 struct cr_regs tmp;
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200410 struct cr_regs *p = crs;
411
412 clk_enable(obj->clk);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200413 iotlb_lock_get(obj, &saved);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200414
Hiroshi DOYU37c28362010-04-27 05:37:12 +0000415 for_each_iotlb_cr(obj, num, i, tmp) {
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200416 if (!iotlb_cr_valid(&tmp))
417 continue;
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200418 *p++ = tmp;
419 }
Hiroshi DOYU37c28362010-04-27 05:37:12 +0000420
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200421 iotlb_lock_set(obj, &saved);
422 clk_disable(obj->clk);
423
424 return p - crs;
425}
426
427/**
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300428 * omap_dump_tlb_entries - dump cr arrays to given buffer
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200429 * @obj: target iommu
430 * @buf: output buffer
431 **/
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300432size_t omap_dump_tlb_entries(struct omap_iommu *obj, char *buf, ssize_t bytes)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200433{
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700434 int i, num;
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200435 struct cr_regs *cr;
436 char *p = buf;
437
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700438 num = bytes / sizeof(*cr);
439 num = min(obj->nr_tlb_entries, num);
440
441 cr = kcalloc(num, sizeof(*cr), GFP_KERNEL);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200442 if (!cr)
443 return 0;
444
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700445 num = __dump_tlb_entries(obj, cr, num);
446 for (i = 0; i < num; i++)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200447 p += iotlb_dump_cr(obj, cr + i, p);
448 kfree(cr);
449
450 return p - buf;
451}
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300452EXPORT_SYMBOL_GPL(omap_dump_tlb_entries);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200453
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300454int omap_foreach_iommu_device(void *data, int (*fn)(struct device *, void *))
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200455{
456 return driver_for_each_device(&omap_iommu_driver.driver,
457 NULL, data, fn);
458}
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300459EXPORT_SYMBOL_GPL(omap_foreach_iommu_device);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200460
461#endif /* CONFIG_OMAP_IOMMU_DEBUG_MODULE */
462
463/*
464 * H/W pagetable operations
465 */
466static void flush_iopgd_range(u32 *first, u32 *last)
467{
468 /* FIXME: L2 cache should be taken care of if it exists */
469 do {
470 asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pgd"
471 : : "r" (first));
472 first += L1_CACHE_BYTES / sizeof(*first);
473 } while (first <= last);
474}
475
476static void flush_iopte_range(u32 *first, u32 *last)
477{
478 /* FIXME: L2 cache should be taken care of if it exists */
479 do {
480 asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pte"
481 : : "r" (first));
482 first += L1_CACHE_BYTES / sizeof(*first);
483 } while (first <= last);
484}
485
486static void iopte_free(u32 *iopte)
487{
488 /* Note: freed iopte's must be clean ready for re-use */
489 kmem_cache_free(iopte_cachep, iopte);
490}
491
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300492static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd, u32 da)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200493{
494 u32 *iopte;
495
496 /* a table has already existed */
497 if (*iopgd)
498 goto pte_ready;
499
500 /*
501 * do the allocation outside the page table lock
502 */
503 spin_unlock(&obj->page_table_lock);
504 iopte = kmem_cache_zalloc(iopte_cachep, GFP_KERNEL);
505 spin_lock(&obj->page_table_lock);
506
507 if (!*iopgd) {
508 if (!iopte)
509 return ERR_PTR(-ENOMEM);
510
511 *iopgd = virt_to_phys(iopte) | IOPGD_TABLE;
512 flush_iopgd_range(iopgd, iopgd);
513
514 dev_vdbg(obj->dev, "%s: a new pte:%p\n", __func__, iopte);
515 } else {
516 /* We raced, free the reduniovant table */
517 iopte_free(iopte);
518 }
519
520pte_ready:
521 iopte = iopte_offset(iopgd, da);
522
523 dev_vdbg(obj->dev,
524 "%s: da:%08x pgd:%p *pgd:%08x pte:%p *pte:%08x\n",
525 __func__, da, iopgd, *iopgd, iopte, *iopte);
526
527 return iopte;
528}
529
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300530static int iopgd_alloc_section(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200531{
532 u32 *iopgd = iopgd_offset(obj, da);
533
Hiroshi DOYU4abb7612010-05-06 18:24:04 +0300534 if ((da | pa) & ~IOSECTION_MASK) {
535 dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
536 __func__, da, pa, IOSECTION_SIZE);
537 return -EINVAL;
538 }
539
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200540 *iopgd = (pa & IOSECTION_MASK) | prot | IOPGD_SECTION;
541 flush_iopgd_range(iopgd, iopgd);
542 return 0;
543}
544
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300545static int iopgd_alloc_super(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200546{
547 u32 *iopgd = iopgd_offset(obj, da);
548 int i;
549
Hiroshi DOYU4abb7612010-05-06 18:24:04 +0300550 if ((da | pa) & ~IOSUPER_MASK) {
551 dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
552 __func__, da, pa, IOSUPER_SIZE);
553 return -EINVAL;
554 }
555
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200556 for (i = 0; i < 16; i++)
557 *(iopgd + i) = (pa & IOSUPER_MASK) | prot | IOPGD_SUPER;
558 flush_iopgd_range(iopgd, iopgd + 15);
559 return 0;
560}
561
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300562static int iopte_alloc_page(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200563{
564 u32 *iopgd = iopgd_offset(obj, da);
565 u32 *iopte = iopte_alloc(obj, iopgd, da);
566
567 if (IS_ERR(iopte))
568 return PTR_ERR(iopte);
569
570 *iopte = (pa & IOPAGE_MASK) | prot | IOPTE_SMALL;
571 flush_iopte_range(iopte, iopte);
572
573 dev_vdbg(obj->dev, "%s: da:%08x pa:%08x pte:%p *pte:%08x\n",
574 __func__, da, pa, iopte, *iopte);
575
576 return 0;
577}
578
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300579static int iopte_alloc_large(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200580{
581 u32 *iopgd = iopgd_offset(obj, da);
582 u32 *iopte = iopte_alloc(obj, iopgd, da);
583 int i;
584
Hiroshi DOYU4abb7612010-05-06 18:24:04 +0300585 if ((da | pa) & ~IOLARGE_MASK) {
586 dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
587 __func__, da, pa, IOLARGE_SIZE);
588 return -EINVAL;
589 }
590
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200591 if (IS_ERR(iopte))
592 return PTR_ERR(iopte);
593
594 for (i = 0; i < 16; i++)
595 *(iopte + i) = (pa & IOLARGE_MASK) | prot | IOPTE_LARGE;
596 flush_iopte_range(iopte, iopte + 15);
597 return 0;
598}
599
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300600static int
601iopgtable_store_entry_core(struct omap_iommu *obj, struct iotlb_entry *e)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200602{
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300603 int (*fn)(struct omap_iommu *, u32, u32, u32);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200604 u32 prot;
605 int err;
606
607 if (!obj || !e)
608 return -EINVAL;
609
610 switch (e->pgsz) {
611 case MMU_CAM_PGSZ_16M:
612 fn = iopgd_alloc_super;
613 break;
614 case MMU_CAM_PGSZ_1M:
615 fn = iopgd_alloc_section;
616 break;
617 case MMU_CAM_PGSZ_64K:
618 fn = iopte_alloc_large;
619 break;
620 case MMU_CAM_PGSZ_4K:
621 fn = iopte_alloc_page;
622 break;
623 default:
624 fn = NULL;
625 BUG();
626 break;
627 }
628
629 prot = get_iopte_attr(e);
630
631 spin_lock(&obj->page_table_lock);
632 err = fn(obj, e->da, e->pa, prot);
633 spin_unlock(&obj->page_table_lock);
634
635 return err;
636}
637
638/**
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300639 * omap_iopgtable_store_entry - Make an iommu pte entry
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200640 * @obj: target iommu
641 * @e: an iommu tlb entry info
642 **/
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300643int omap_iopgtable_store_entry(struct omap_iommu *obj, struct iotlb_entry *e)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200644{
645 int err;
646
647 flush_iotlb_page(obj, e->da);
648 err = iopgtable_store_entry_core(obj, e);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200649 if (!err)
Ohad Ben-Cohen5da14a42011-08-16 15:19:10 +0300650 prefetch_iotlb_entry(obj, e);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200651 return err;
652}
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300653EXPORT_SYMBOL_GPL(omap_iopgtable_store_entry);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200654
655/**
656 * iopgtable_lookup_entry - Lookup an iommu pte entry
657 * @obj: target iommu
658 * @da: iommu device virtual address
659 * @ppgd: iommu pgd entry pointer to be returned
660 * @ppte: iommu pte entry pointer to be returned
661 **/
Ohad Ben-Cohene1f23812011-08-16 14:58:14 +0300662static void
663iopgtable_lookup_entry(struct omap_iommu *obj, u32 da, u32 **ppgd, u32 **ppte)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200664{
665 u32 *iopgd, *iopte = NULL;
666
667 iopgd = iopgd_offset(obj, da);
668 if (!*iopgd)
669 goto out;
670
Hiroshi DOYUa1a54452010-05-13 09:45:35 +0300671 if (iopgd_is_table(*iopgd))
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200672 iopte = iopte_offset(iopgd, da);
673out:
674 *ppgd = iopgd;
675 *ppte = iopte;
676}
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200677
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300678static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200679{
680 size_t bytes;
681 u32 *iopgd = iopgd_offset(obj, da);
682 int nent = 1;
683
684 if (!*iopgd)
685 return 0;
686
Hiroshi DOYUa1a54452010-05-13 09:45:35 +0300687 if (iopgd_is_table(*iopgd)) {
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200688 int i;
689 u32 *iopte = iopte_offset(iopgd, da);
690
691 bytes = IOPTE_SIZE;
692 if (*iopte & IOPTE_LARGE) {
693 nent *= 16;
694 /* rewind to the 1st entry */
Hiroshi DOYUc127c7d2010-02-15 10:03:32 -0800695 iopte = iopte_offset(iopgd, (da & IOLARGE_MASK));
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200696 }
697 bytes *= nent;
698 memset(iopte, 0, nent * sizeof(*iopte));
699 flush_iopte_range(iopte, iopte + (nent - 1) * sizeof(*iopte));
700
701 /*
702 * do table walk to check if this table is necessary or not
703 */
704 iopte = iopte_offset(iopgd, 0);
705 for (i = 0; i < PTRS_PER_IOPTE; i++)
706 if (iopte[i])
707 goto out;
708
709 iopte_free(iopte);
710 nent = 1; /* for the next L1 entry */
711 } else {
712 bytes = IOPGD_SIZE;
Hiroshi DOYUdcc730d2009-10-22 14:46:32 -0700713 if ((*iopgd & IOPGD_SUPER) == IOPGD_SUPER) {
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200714 nent *= 16;
715 /* rewind to the 1st entry */
Hiroshi DOYU8d33ea52010-02-15 10:03:32 -0800716 iopgd = iopgd_offset(obj, (da & IOSUPER_MASK));
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200717 }
718 bytes *= nent;
719 }
720 memset(iopgd, 0, nent * sizeof(*iopgd));
721 flush_iopgd_range(iopgd, iopgd + (nent - 1) * sizeof(*iopgd));
722out:
723 return bytes;
724}
725
726/**
727 * iopgtable_clear_entry - Remove an iommu pte entry
728 * @obj: target iommu
729 * @da: iommu device virtual address
730 **/
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300731static size_t iopgtable_clear_entry(struct omap_iommu *obj, u32 da)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200732{
733 size_t bytes;
734
735 spin_lock(&obj->page_table_lock);
736
737 bytes = iopgtable_clear_entry_core(obj, da);
738 flush_iotlb_page(obj, da);
739
740 spin_unlock(&obj->page_table_lock);
741
742 return bytes;
743}
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200744
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300745static void iopgtable_clear_entry_all(struct omap_iommu *obj)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200746{
747 int i;
748
749 spin_lock(&obj->page_table_lock);
750
751 for (i = 0; i < PTRS_PER_IOPGD; i++) {
752 u32 da;
753 u32 *iopgd;
754
755 da = i << IOPGD_SHIFT;
756 iopgd = iopgd_offset(obj, da);
757
758 if (!*iopgd)
759 continue;
760
Hiroshi DOYUa1a54452010-05-13 09:45:35 +0300761 if (iopgd_is_table(*iopgd))
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200762 iopte_free(iopte_offset(iopgd, 0));
763
764 *iopgd = 0;
765 flush_iopgd_range(iopgd, iopgd);
766 }
767
768 flush_iotlb_all(obj);
769
770 spin_unlock(&obj->page_table_lock);
771}
772
773/*
774 * Device IOMMU generic operations
775 */
776static irqreturn_t iommu_fault_handler(int irq, void *data)
777{
David Cohend594f1f2011-02-16 19:35:51 +0000778 u32 da, errs;
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200779 u32 *iopgd, *iopte;
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300780 struct omap_iommu *obj = data;
Ohad Ben-Cohene7f10f02011-09-13 15:26:29 -0400781 struct iommu_domain *domain = obj->domain;
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200782
783 if (!obj->refcount)
784 return IRQ_NONE;
785
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200786 clk_enable(obj->clk);
David Cohend594f1f2011-02-16 19:35:51 +0000787 errs = iommu_report_fault(obj, &da);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200788 clk_disable(obj->clk);
Laurent Pinchartc56b2dd2011-05-10 16:56:46 +0200789 if (errs == 0)
790 return IRQ_HANDLED;
David Cohend594f1f2011-02-16 19:35:51 +0000791
792 /* Fault callback or TLB/PTE Dynamic loading */
Ohad Ben-Cohene7f10f02011-09-13 15:26:29 -0400793 if (!report_iommu_fault(domain, obj->dev, da, 0))
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200794 return IRQ_HANDLED;
795
Hiroshi DOYU37b29812010-05-24 02:01:52 +0000796 iommu_disable(obj);
797
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200798 iopgd = iopgd_offset(obj, da);
799
Hiroshi DOYUa1a54452010-05-13 09:45:35 +0300800 if (!iopgd_is_table(*iopgd)) {
David Cohend594f1f2011-02-16 19:35:51 +0000801 dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p "
802 "*pgd:px%08x\n", obj->name, errs, da, iopgd, *iopgd);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200803 return IRQ_NONE;
804 }
805
806 iopte = iopte_offset(iopgd, da);
807
David Cohend594f1f2011-02-16 19:35:51 +0000808 dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p *pgd:0x%08x "
809 "pte:0x%p *pte:0x%08x\n", obj->name, errs, da, iopgd, *iopgd,
810 iopte, *iopte);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200811
812 return IRQ_NONE;
813}
814
815static int device_match_by_alias(struct device *dev, void *data)
816{
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300817 struct omap_iommu *obj = to_iommu(dev);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200818 const char *name = data;
819
820 pr_debug("%s: %s %s\n", __func__, obj->name, name);
821
822 return strcmp(obj->name, name) == 0;
823}
824
825/**
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +0300826 * omap_find_iommu_device() - find an omap iommu device by name
827 * @name: name of the iommu device
828 *
829 * The generic iommu API requires the caller to provide the device
830 * he wishes to attach to a certain iommu domain.
831 *
832 * Drivers generally should not bother with this as it should just
833 * be taken care of by the DMA-API using dev_archdata.
834 *
835 * This function is provided as an interim solution until the latter
836 * materializes, and omap3isp is fully migrated to the DMA-API.
837 */
838struct device *omap_find_iommu_device(const char *name)
Guzman Lugo, Fernandoc7f4ab22010-12-15 00:54:03 +0000839{
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +0300840 return driver_find_device(&omap_iommu_driver.driver, NULL,
841 (void *)name,
842 device_match_by_alias);
Guzman Lugo, Fernandoc7f4ab22010-12-15 00:54:03 +0000843}
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +0300844EXPORT_SYMBOL_GPL(omap_find_iommu_device);
Guzman Lugo, Fernandoc7f4ab22010-12-15 00:54:03 +0000845
846/**
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +0300847 * omap_iommu_attach() - attach iommu device to an iommu domain
848 * @dev: target omap iommu device
849 * @iopgd: page table
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200850 **/
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300851static struct omap_iommu *omap_iommu_attach(struct device *dev, u32 *iopgd)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200852{
853 int err = -ENOMEM;
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300854 struct omap_iommu *obj = to_iommu(dev);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200855
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +0300856 spin_lock(&obj->iommu_lock);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200857
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +0300858 /* an iommu device can only be attached once */
859 if (++obj->refcount > 1) {
860 dev_err(dev, "%s: already attached!\n", obj->name);
861 err = -EBUSY;
862 goto err_enable;
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200863 }
864
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +0300865 obj->iopgd = iopgd;
866 err = iommu_enable(obj);
867 if (err)
868 goto err_enable;
869 flush_iotlb_all(obj);
870
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200871 if (!try_module_get(obj->owner))
872 goto err_module;
873
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +0300874 spin_unlock(&obj->iommu_lock);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200875
876 dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
877 return obj;
878
879err_module:
880 if (obj->refcount == 1)
881 iommu_disable(obj);
882err_enable:
883 obj->refcount--;
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +0300884 spin_unlock(&obj->iommu_lock);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200885 return ERR_PTR(err);
886}
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200887
888/**
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +0300889 * omap_iommu_detach - release iommu device
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200890 * @obj: target iommu
891 **/
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300892static void omap_iommu_detach(struct omap_iommu *obj)
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200893{
Roel Kluinacf9d462010-01-08 10:29:05 -0800894 if (!obj || IS_ERR(obj))
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200895 return;
896
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +0300897 spin_lock(&obj->iommu_lock);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200898
899 if (--obj->refcount == 0)
900 iommu_disable(obj);
901
902 module_put(obj->owner);
903
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +0300904 obj->iopgd = NULL;
905
906 spin_unlock(&obj->iommu_lock);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200907
908 dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
909}
David Cohend594f1f2011-02-16 19:35:51 +0000910
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200911/*
912 * OMAP Device MMU(IOMMU) detection
913 */
914static int __devinit omap_iommu_probe(struct platform_device *pdev)
915{
916 int err = -ENODEV;
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200917 int irq;
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300918 struct omap_iommu *obj;
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200919 struct resource *res;
920 struct iommu_platform_data *pdata = pdev->dev.platform_data;
921
922 if (pdev->num_resources != 2)
923 return -EINVAL;
924
925 obj = kzalloc(sizeof(*obj) + MMU_REG_SIZE, GFP_KERNEL);
926 if (!obj)
927 return -ENOMEM;
928
929 obj->clk = clk_get(&pdev->dev, pdata->clk_name);
930 if (IS_ERR(obj->clk))
931 goto err_clk;
932
933 obj->nr_tlb_entries = pdata->nr_tlb_entries;
934 obj->name = pdata->name;
935 obj->dev = &pdev->dev;
936 obj->ctx = (void *)obj + sizeof(*obj);
Guzman Lugo, Fernandoc7f4ab22010-12-15 00:54:03 +0000937 obj->da_start = pdata->da_start;
938 obj->da_end = pdata->da_end;
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200939
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +0300940 spin_lock_init(&obj->iommu_lock);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200941 mutex_init(&obj->mmap_lock);
942 spin_lock_init(&obj->page_table_lock);
943 INIT_LIST_HEAD(&obj->mmap);
944
945 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
946 if (!res) {
947 err = -ENODEV;
948 goto err_mem;
949 }
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200950
951 res = request_mem_region(res->start, resource_size(res),
952 dev_name(&pdev->dev));
953 if (!res) {
954 err = -EIO;
955 goto err_mem;
956 }
957
Aaro Koskinenda4a0f72011-03-14 12:28:32 +0000958 obj->regbase = ioremap(res->start, resource_size(res));
959 if (!obj->regbase) {
960 err = -ENOMEM;
961 goto err_ioremap;
962 }
963
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200964 irq = platform_get_irq(pdev, 0);
965 if (irq < 0) {
966 err = -ENODEV;
967 goto err_irq;
968 }
969 err = request_irq(irq, iommu_fault_handler, IRQF_SHARED,
970 dev_name(&pdev->dev), obj);
971 if (err < 0)
972 goto err_irq;
973 platform_set_drvdata(pdev, obj);
974
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200975 dev_info(&pdev->dev, "%s registered\n", obj->name);
976 return 0;
977
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200978err_irq:
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200979 iounmap(obj->regbase);
Aaro Koskinenda4a0f72011-03-14 12:28:32 +0000980err_ioremap:
981 release_mem_region(res->start, resource_size(res));
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200982err_mem:
983 clk_put(obj->clk);
984err_clk:
985 kfree(obj);
986 return err;
987}
988
989static int __devexit omap_iommu_remove(struct platform_device *pdev)
990{
991 int irq;
992 struct resource *res;
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300993 struct omap_iommu *obj = platform_get_drvdata(pdev);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200994
995 platform_set_drvdata(pdev, NULL);
996
997 iopgtable_clear_entry_all(obj);
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +0200998
999 irq = platform_get_irq(pdev, 0);
1000 free_irq(irq, obj);
1001 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1002 release_mem_region(res->start, resource_size(res));
1003 iounmap(obj->regbase);
1004
1005 clk_put(obj->clk);
1006 dev_info(&pdev->dev, "%s removed\n", obj->name);
1007 kfree(obj);
1008 return 0;
1009}
1010
1011static struct platform_driver omap_iommu_driver = {
1012 .probe = omap_iommu_probe,
1013 .remove = __devexit_p(omap_iommu_remove),
1014 .driver = {
1015 .name = "omap-iommu",
1016 },
1017};
1018
1019static void iopte_cachep_ctor(void *iopte)
1020{
1021 clean_dcache_area(iopte, IOPTE_TABLE_SIZE);
1022}
1023
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001024static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
Ohad Ben-Cohen50090652011-11-10 11:32:25 +02001025 phys_addr_t pa, size_t bytes, int prot)
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001026{
1027 struct omap_iommu_domain *omap_domain = domain->priv;
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +03001028 struct omap_iommu *oiommu = omap_domain->iommu_dev;
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001029 struct device *dev = oiommu->dev;
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001030 struct iotlb_entry e;
1031 int omap_pgsz;
1032 u32 ret, flags;
1033
1034 /* we only support mapping a single iommu page for now */
1035 omap_pgsz = bytes_to_iopgsz(bytes);
1036 if (omap_pgsz < 0) {
1037 dev_err(dev, "invalid size to map: %d\n", bytes);
1038 return -EINVAL;
1039 }
1040
1041 dev_dbg(dev, "mapping da 0x%lx to pa 0x%x size 0x%x\n", da, pa, bytes);
1042
1043 flags = omap_pgsz | prot;
1044
1045 iotlb_init_entry(&e, da, pa, flags);
1046
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +03001047 ret = omap_iopgtable_store_entry(oiommu, &e);
Ohad Ben-Cohenb4550d42011-09-02 13:32:31 -04001048 if (ret)
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +03001049 dev_err(dev, "omap_iopgtable_store_entry failed: %d\n", ret);
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001050
Ohad Ben-Cohenb4550d42011-09-02 13:32:31 -04001051 return ret;
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001052}
1053
Ohad Ben-Cohen50090652011-11-10 11:32:25 +02001054static size_t omap_iommu_unmap(struct iommu_domain *domain, unsigned long da,
1055 size_t size)
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001056{
1057 struct omap_iommu_domain *omap_domain = domain->priv;
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +03001058 struct omap_iommu *oiommu = omap_domain->iommu_dev;
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001059 struct device *dev = oiommu->dev;
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001060
Ohad Ben-Cohen50090652011-11-10 11:32:25 +02001061 dev_dbg(dev, "unmapping da 0x%lx size %u\n", da, size);
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001062
Ohad Ben-Cohen50090652011-11-10 11:32:25 +02001063 return iopgtable_clear_entry(oiommu, da);
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001064}
1065
1066static int
1067omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
1068{
1069 struct omap_iommu_domain *omap_domain = domain->priv;
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +03001070 struct omap_iommu *oiommu;
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001071 int ret = 0;
1072
1073 spin_lock(&omap_domain->lock);
1074
1075 /* only a single device is supported per domain for now */
1076 if (omap_domain->iommu_dev) {
1077 dev_err(dev, "iommu domain is already attached\n");
1078 ret = -EBUSY;
1079 goto out;
1080 }
1081
1082 /* get a handle to and enable the omap iommu */
1083 oiommu = omap_iommu_attach(dev, omap_domain->pgtable);
1084 if (IS_ERR(oiommu)) {
1085 ret = PTR_ERR(oiommu);
1086 dev_err(dev, "can't get omap iommu: %d\n", ret);
1087 goto out;
1088 }
1089
1090 omap_domain->iommu_dev = oiommu;
Ohad Ben-Cohene7f10f02011-09-13 15:26:29 -04001091 oiommu->domain = domain;
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001092
1093out:
1094 spin_unlock(&omap_domain->lock);
1095 return ret;
1096}
1097
1098static void omap_iommu_detach_dev(struct iommu_domain *domain,
1099 struct device *dev)
1100{
1101 struct omap_iommu_domain *omap_domain = domain->priv;
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +03001102 struct omap_iommu *oiommu = to_iommu(dev);
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001103
1104 spin_lock(&omap_domain->lock);
1105
1106 /* only a single device is supported per domain for now */
1107 if (omap_domain->iommu_dev != oiommu) {
1108 dev_err(dev, "invalid iommu device\n");
1109 goto out;
1110 }
1111
1112 iopgtable_clear_entry_all(oiommu);
1113
1114 omap_iommu_detach(oiommu);
1115
1116 omap_domain->iommu_dev = NULL;
1117
1118out:
1119 spin_unlock(&omap_domain->lock);
1120}
1121
1122static int omap_iommu_domain_init(struct iommu_domain *domain)
1123{
1124 struct omap_iommu_domain *omap_domain;
1125
1126 omap_domain = kzalloc(sizeof(*omap_domain), GFP_KERNEL);
1127 if (!omap_domain) {
1128 pr_err("kzalloc failed\n");
1129 goto out;
1130 }
1131
1132 omap_domain->pgtable = kzalloc(IOPGD_TABLE_SIZE, GFP_KERNEL);
1133 if (!omap_domain->pgtable) {
1134 pr_err("kzalloc failed\n");
1135 goto fail_nomem;
1136 }
1137
1138 /*
1139 * should never fail, but please keep this around to ensure
1140 * we keep the hardware happy
1141 */
1142 BUG_ON(!IS_ALIGNED((long)omap_domain->pgtable, IOPGD_TABLE_SIZE));
1143
1144 clean_dcache_area(omap_domain->pgtable, IOPGD_TABLE_SIZE);
1145 spin_lock_init(&omap_domain->lock);
1146
1147 domain->priv = omap_domain;
1148
1149 return 0;
1150
1151fail_nomem:
1152 kfree(omap_domain);
1153out:
1154 return -ENOMEM;
1155}
1156
1157/* assume device was already detached */
1158static void omap_iommu_domain_destroy(struct iommu_domain *domain)
1159{
1160 struct omap_iommu_domain *omap_domain = domain->priv;
1161
1162 domain->priv = NULL;
1163
1164 kfree(omap_domain->pgtable);
1165 kfree(omap_domain);
1166}
1167
1168static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain,
1169 unsigned long da)
1170{
1171 struct omap_iommu_domain *omap_domain = domain->priv;
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +03001172 struct omap_iommu *oiommu = omap_domain->iommu_dev;
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001173 struct device *dev = oiommu->dev;
1174 u32 *pgd, *pte;
1175 phys_addr_t ret = 0;
1176
1177 iopgtable_lookup_entry(oiommu, da, &pgd, &pte);
1178
1179 if (pte) {
1180 if (iopte_is_small(*pte))
1181 ret = omap_iommu_translate(*pte, da, IOPTE_MASK);
1182 else if (iopte_is_large(*pte))
1183 ret = omap_iommu_translate(*pte, da, IOLARGE_MASK);
1184 else
1185 dev_err(dev, "bogus pte 0x%x", *pte);
1186 } else {
1187 if (iopgd_is_section(*pgd))
1188 ret = omap_iommu_translate(*pgd, da, IOSECTION_MASK);
1189 else if (iopgd_is_super(*pgd))
1190 ret = omap_iommu_translate(*pgd, da, IOSUPER_MASK);
1191 else
1192 dev_err(dev, "bogus pgd 0x%x", *pgd);
1193 }
1194
1195 return ret;
1196}
1197
1198static int omap_iommu_domain_has_cap(struct iommu_domain *domain,
1199 unsigned long cap)
1200{
1201 return 0;
1202}
1203
1204static struct iommu_ops omap_iommu_ops = {
1205 .domain_init = omap_iommu_domain_init,
1206 .domain_destroy = omap_iommu_domain_destroy,
1207 .attach_dev = omap_iommu_attach_dev,
1208 .detach_dev = omap_iommu_detach_dev,
1209 .map = omap_iommu_map,
1210 .unmap = omap_iommu_unmap,
1211 .iova_to_phys = omap_iommu_iova_to_phys,
1212 .domain_has_cap = omap_iommu_domain_has_cap,
Ohad Ben-Cohen66bc8cf2011-11-10 11:32:27 +02001213 .pgsize_bitmap = OMAP_IOMMU_PGSIZES,
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001214};
1215
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +02001216static int __init omap_iommu_init(void)
1217{
1218 struct kmem_cache *p;
1219 const unsigned long flags = SLAB_HWCACHE_ALIGN;
1220 size_t align = 1 << 10; /* L2 pagetable alignement */
1221
1222 p = kmem_cache_create("iopte_cache", IOPTE_TABLE_SIZE, align, flags,
1223 iopte_cachep_ctor);
1224 if (!p)
1225 return -ENOMEM;
1226 iopte_cachep = p;
1227
Joerg Roedela65bc642011-09-06 17:56:07 +02001228 bus_set_iommu(&platform_bus_type, &omap_iommu_ops);
Ohad Ben-Cohenf626b522011-06-02 01:46:12 +03001229
Hiroshi DOYUa9dcad52009-01-26 15:13:40 +02001230 return platform_driver_register(&omap_iommu_driver);
1231}
1232module_init(omap_iommu_init);
1233
1234static void __exit omap_iommu_exit(void)
1235{
1236 kmem_cache_destroy(iopte_cachep);
1237
1238 platform_driver_unregister(&omap_iommu_driver);
1239}
1240module_exit(omap_iommu_exit);
1241
1242MODULE_DESCRIPTION("omap iommu: tlb and pagetable primitives");
1243MODULE_ALIAS("platform:omap-iommu");
1244MODULE_AUTHOR("Hiroshi DOYU, Paul Mundt and Toshihiro Kobayashi");
1245MODULE_LICENSE("GPL v2");