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