Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394...
[pandora-kernel.git] / arch / arm / plat-omap / iommu-debug.c
1 /*
2  * omap iommu: debugfs interface
3  *
4  * Copyright (C) 2008-2009 Nokia Corporation
5  *
6  * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12
13 #include <linux/err.h>
14 #include <linux/clk.h>
15 #include <linux/io.h>
16 #include <linux/uaccess.h>
17 #include <linux/platform_device.h>
18 #include <linux/debugfs.h>
19
20 #include <mach/iommu.h>
21 #include <mach/iovmm.h>
22
23 #include "iopgtable.h"
24
25 #define MAXCOLUMN 100 /* for short messages */
26
27 static DEFINE_MUTEX(iommu_debug_lock);
28
29 static struct dentry *iommu_debug_root;
30
31 static ssize_t debug_read_ver(struct file *file, char __user *userbuf,
32                               size_t count, loff_t *ppos)
33 {
34         u32 ver = iommu_arch_version();
35         char buf[MAXCOLUMN], *p = buf;
36
37         p += sprintf(p, "H/W version: %d.%d\n", (ver >> 4) & 0xf , ver & 0xf);
38
39         return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
40 }
41
42 static ssize_t debug_read_regs(struct file *file, char __user *userbuf,
43                                size_t count, loff_t *ppos)
44 {
45         struct iommu *obj = file->private_data;
46         char *p, *buf;
47         ssize_t bytes;
48
49         buf = kmalloc(count, GFP_KERNEL);
50         if (!buf)
51                 return -ENOMEM;
52         p = buf;
53
54         mutex_lock(&iommu_debug_lock);
55
56         bytes = iommu_dump_ctx(obj, p, count);
57         bytes = simple_read_from_buffer(userbuf, count, ppos, buf, bytes);
58
59         mutex_unlock(&iommu_debug_lock);
60         kfree(buf);
61
62         return bytes;
63 }
64
65 static ssize_t debug_read_tlb(struct file *file, char __user *userbuf,
66                               size_t count, loff_t *ppos)
67 {
68         struct iommu *obj = file->private_data;
69         char *p, *buf;
70         ssize_t bytes, rest;
71
72         buf = kmalloc(count, GFP_KERNEL);
73         if (!buf)
74                 return -ENOMEM;
75         p = buf;
76
77         mutex_lock(&iommu_debug_lock);
78
79         p += sprintf(p, "%8s %8s\n", "cam:", "ram:");
80         p += sprintf(p, "-----------------------------------------\n");
81         rest = count - (p - buf);
82         p += dump_tlb_entries(obj, p, rest);
83
84         bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
85
86         mutex_unlock(&iommu_debug_lock);
87         kfree(buf);
88
89         return bytes;
90 }
91
92 static ssize_t debug_write_pagetable(struct file *file,
93                      const char __user *userbuf, size_t count, loff_t *ppos)
94 {
95         struct iotlb_entry e;
96         struct cr_regs cr;
97         int err;
98         struct iommu *obj = file->private_data;
99         char buf[MAXCOLUMN], *p = buf;
100
101         count = min(count, sizeof(buf));
102
103         mutex_lock(&iommu_debug_lock);
104         if (copy_from_user(p, userbuf, count)) {
105                 mutex_unlock(&iommu_debug_lock);
106                 return -EFAULT;
107         }
108
109         sscanf(p, "%x %x", &cr.cam, &cr.ram);
110         if (!cr.cam || !cr.ram) {
111                 mutex_unlock(&iommu_debug_lock);
112                 return -EINVAL;
113         }
114
115         iotlb_cr_to_e(&cr, &e);
116         err = iopgtable_store_entry(obj, &e);
117         if (err)
118                 dev_err(obj->dev, "%s: fail to store cr\n", __func__);
119
120         mutex_unlock(&iommu_debug_lock);
121         return count;
122 }
123
124 #define dump_ioptable_entry_one(lv, da, val)                    \
125         ({                                                      \
126                 int __err = 0;                                  \
127                 ssize_t bytes;                                  \
128                 const int maxcol = 22;                          \
129                 const char *str = "%d: %08x %08x\n";            \
130                 bytes = snprintf(p, maxcol, str, lv, da, val);  \
131                 p += bytes;                                     \
132                 len -= bytes;                                   \
133                 if (len < maxcol)                               \
134                         __err = -ENOMEM;                        \
135                 __err;                                          \
136         })
137
138 static ssize_t dump_ioptable(struct iommu *obj, char *buf, ssize_t len)
139 {
140         int i;
141         u32 *iopgd;
142         char *p = buf;
143
144         spin_lock(&obj->page_table_lock);
145
146         iopgd = iopgd_offset(obj, 0);
147         for (i = 0; i < PTRS_PER_IOPGD; i++, iopgd++) {
148                 int j, err;
149                 u32 *iopte;
150                 u32 da;
151
152                 if (!*iopgd)
153                         continue;
154
155                 if (!(*iopgd & IOPGD_TABLE)) {
156                         da = i << IOPGD_SHIFT;
157
158                         err = dump_ioptable_entry_one(1, da, *iopgd);
159                         if (err)
160                                 goto out;
161                         continue;
162                 }
163
164                 iopte = iopte_offset(iopgd, 0);
165
166                 for (j = 0; j < PTRS_PER_IOPTE; j++, iopte++) {
167                         if (!*iopte)
168                                 continue;
169
170                         da = (i << IOPGD_SHIFT) + (j << IOPTE_SHIFT);
171                         err = dump_ioptable_entry_one(2, da, *iopgd);
172                         if (err)
173                                 goto out;
174                 }
175         }
176 out:
177         spin_unlock(&obj->page_table_lock);
178
179         return p - buf;
180 }
181
182 static ssize_t debug_read_pagetable(struct file *file, char __user *userbuf,
183                                     size_t count, loff_t *ppos)
184 {
185         struct iommu *obj = file->private_data;
186         char *p, *buf;
187         size_t bytes;
188
189         buf = (char *)__get_free_page(GFP_KERNEL);
190         if (!buf)
191                 return -ENOMEM;
192         p = buf;
193
194         p += sprintf(p, "L: %8s %8s\n", "da:", "pa:");
195         p += sprintf(p, "-----------------------------------------\n");
196
197         mutex_lock(&iommu_debug_lock);
198
199         bytes = PAGE_SIZE - (p - buf);
200         p += dump_ioptable(obj, p, bytes);
201
202         bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
203
204         mutex_unlock(&iommu_debug_lock);
205         free_page((unsigned long)buf);
206
207         return bytes;
208 }
209
210 static ssize_t debug_read_mmap(struct file *file, char __user *userbuf,
211                                size_t count, loff_t *ppos)
212 {
213         struct iommu *obj = file->private_data;
214         char *p, *buf;
215         struct iovm_struct *tmp;
216         int uninitialized_var(i);
217         ssize_t bytes;
218
219         buf = (char *)__get_free_page(GFP_KERNEL);
220         if (!buf)
221                 return -ENOMEM;
222         p = buf;
223
224         p += sprintf(p, "%-3s %-8s %-8s %6s %8s\n",
225                      "No", "start", "end", "size", "flags");
226         p += sprintf(p, "-------------------------------------------------\n");
227
228         mutex_lock(&iommu_debug_lock);
229
230         list_for_each_entry(tmp, &obj->mmap, list) {
231                 size_t len;
232                 const char *str = "%3d %08x-%08x %6x %8x\n";
233                 const int maxcol = 39;
234
235                 len = tmp->da_end - tmp->da_start;
236                 p += snprintf(p, maxcol, str,
237                               i, tmp->da_start, tmp->da_end, len, tmp->flags);
238
239                 if (PAGE_SIZE - (p - buf) < maxcol)
240                         break;
241                 i++;
242         }
243
244         bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
245
246         mutex_unlock(&iommu_debug_lock);
247         free_page((unsigned long)buf);
248
249         return bytes;
250 }
251
252 static ssize_t debug_read_mem(struct file *file, char __user *userbuf,
253                               size_t count, loff_t *ppos)
254 {
255         struct iommu *obj = file->private_data;
256         char *p, *buf;
257         struct iovm_struct *area;
258         ssize_t bytes;
259
260         count = min_t(ssize_t, count, PAGE_SIZE);
261
262         buf = (char *)__get_free_page(GFP_KERNEL);
263         if (!buf)
264                 return -ENOMEM;
265         p = buf;
266
267         mutex_lock(&iommu_debug_lock);
268
269         area = find_iovm_area(obj, (u32)ppos);
270         if (IS_ERR(area)) {
271                 bytes = -EINVAL;
272                 goto err_out;
273         }
274         memcpy(p, area->va, count);
275         p += count;
276
277         bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
278 err_out:
279         mutex_unlock(&iommu_debug_lock);
280         free_page((unsigned long)buf);
281
282         return bytes;
283 }
284
285 static ssize_t debug_write_mem(struct file *file, const char __user *userbuf,
286                                size_t count, loff_t *ppos)
287 {
288         struct iommu *obj = file->private_data;
289         struct iovm_struct *area;
290         char *p, *buf;
291
292         count = min_t(size_t, count, PAGE_SIZE);
293
294         buf = (char *)__get_free_page(GFP_KERNEL);
295         if (!buf)
296                 return -ENOMEM;
297         p = buf;
298
299         mutex_lock(&iommu_debug_lock);
300
301         if (copy_from_user(p, userbuf, count)) {
302                 count =  -EFAULT;
303                 goto err_out;
304         }
305
306         area = find_iovm_area(obj, (u32)ppos);
307         if (IS_ERR(area)) {
308                 count = -EINVAL;
309                 goto err_out;
310         }
311         memcpy(area->va, p, count);
312 err_out:
313         mutex_unlock(&iommu_debug_lock);
314         free_page((unsigned long)buf);
315
316         return count;
317 }
318
319 static int debug_open_generic(struct inode *inode, struct file *file)
320 {
321         file->private_data = inode->i_private;
322         return 0;
323 }
324
325 #define DEBUG_FOPS(name)                                                \
326         static const struct file_operations debug_##name##_fops = {     \
327                 .open = debug_open_generic,                             \
328                 .read = debug_read_##name,                              \
329                 .write = debug_write_##name,                            \
330         };
331
332 #define DEBUG_FOPS_RO(name)                                             \
333         static const struct file_operations debug_##name##_fops = {     \
334                 .open = debug_open_generic,                             \
335                 .read = debug_read_##name,                              \
336         };
337
338 DEBUG_FOPS_RO(ver);
339 DEBUG_FOPS_RO(regs);
340 DEBUG_FOPS_RO(tlb);
341 DEBUG_FOPS(pagetable);
342 DEBUG_FOPS_RO(mmap);
343 DEBUG_FOPS(mem);
344
345 #define __DEBUG_ADD_FILE(attr, mode)                                    \
346         {                                                               \
347                 struct dentry *dent;                                    \
348                 dent = debugfs_create_file(#attr, mode, parent,         \
349                                            obj, &debug_##attr##_fops);  \
350                 if (!dent)                                              \
351                         return -ENOMEM;                                 \
352         }
353
354 #define DEBUG_ADD_FILE(name) __DEBUG_ADD_FILE(name, 600)
355 #define DEBUG_ADD_FILE_RO(name) __DEBUG_ADD_FILE(name, 400)
356
357 static int iommu_debug_register(struct device *dev, void *data)
358 {
359         struct platform_device *pdev = to_platform_device(dev);
360         struct iommu *obj = platform_get_drvdata(pdev);
361         struct dentry *d, *parent;
362
363         if (!obj || !obj->dev)
364                 return -EINVAL;
365
366         d = debugfs_create_dir(obj->name, iommu_debug_root);
367         if (!d)
368                 return -ENOMEM;
369         parent = d;
370
371         d = debugfs_create_u8("nr_tlb_entries", 400, parent,
372                               (u8 *)&obj->nr_tlb_entries);
373         if (!d)
374                 return -ENOMEM;
375
376         DEBUG_ADD_FILE_RO(ver);
377         DEBUG_ADD_FILE_RO(regs);
378         DEBUG_ADD_FILE_RO(tlb);
379         DEBUG_ADD_FILE(pagetable);
380         DEBUG_ADD_FILE_RO(mmap);
381         DEBUG_ADD_FILE(mem);
382
383         return 0;
384 }
385
386 static int __init iommu_debug_init(void)
387 {
388         struct dentry *d;
389         int err;
390
391         d = debugfs_create_dir("iommu", NULL);
392         if (!d)
393                 return -ENOMEM;
394         iommu_debug_root = d;
395
396         err = foreach_iommu_device(d, iommu_debug_register);
397         if (err)
398                 goto err_out;
399         return 0;
400
401 err_out:
402         debugfs_remove_recursive(iommu_debug_root);
403         return err;
404 }
405 module_init(iommu_debug_init)
406
407 static void __exit iommu_debugfs_exit(void)
408 {
409         debugfs_remove_recursive(iommu_debug_root);
410 }
411 module_exit(iommu_debugfs_exit)
412
413 MODULE_DESCRIPTION("omap iommu: debugfs interface");
414 MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>");
415 MODULE_LICENSE("GPL v2");