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