blob: d0427bd21361956080706b81dc7fb77fe76b9b0e [file] [log] [blame]
Hiroshi DOYU14e0e672009-08-28 10:54:41 -07001/*
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
Ming Lei08f2e632011-11-08 18:29:15 +080013#include <linux/module.h>
Hiroshi DOYU14e0e672009-08-28 10:54:41 -070014#include <linux/err.h>
15#include <linux/clk.h>
16#include <linux/io.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090017#include <linux/slab.h>
Hiroshi DOYU14e0e672009-08-28 10:54:41 -070018#include <linux/uaccess.h>
19#include <linux/platform_device.h>
20#include <linux/debugfs.h>
Tony Lindgrenc8d35c82012-11-02 12:24:03 -070021#include <linux/omap-iommu.h>
Hiroshi DOYU14e0e672009-08-28 10:54:41 -070022
Tony Lindgrence491cf2009-10-20 09:40:47 -070023#include <plat/iommu.h>
Hiroshi DOYU14e0e672009-08-28 10:54:41 -070024
Ido Yariv2f7702a2012-11-02 12:24:00 -070025#include "omap-iopgtable.h"
Tony Lindgrened1c7de2012-11-02 12:24:06 -070026#include "omap-iommu.h"
Hiroshi DOYU14e0e672009-08-28 10:54:41 -070027
28#define MAXCOLUMN 100 /* for short messages */
29
30static DEFINE_MUTEX(iommu_debug_lock);
31
32static struct dentry *iommu_debug_root;
33
34static ssize_t debug_read_ver(struct file *file, char __user *userbuf,
35 size_t count, loff_t *ppos)
36{
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +030037 u32 ver = omap_iommu_arch_version();
Hiroshi DOYU14e0e672009-08-28 10:54:41 -070038 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
45static ssize_t debug_read_regs(struct file *file, char __user *userbuf,
46 size_t count, loff_t *ppos)
47{
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +020048 struct device *dev = file->private_data;
49 struct omap_iommu *obj = dev_to_omap_iommu(dev);
Hiroshi DOYU14e0e672009-08-28 10:54:41 -070050 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
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +030060 bytes = omap_iommu_dump_ctx(obj, p, count);
Hiroshi DOYU14e0e672009-08-28 10:54:41 -070061 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
69static ssize_t debug_read_tlb(struct file *file, char __user *userbuf,
70 size_t count, loff_t *ppos)
71{
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +020072 struct device *dev = file->private_data;
73 struct omap_iommu *obj = dev_to_omap_iommu(dev);
Hiroshi DOYU14e0e672009-08-28 10:54:41 -070074 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);
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +030087 p += omap_dump_tlb_entries(obj, p, rest);
Hiroshi DOYU14e0e672009-08-28 10:54:41 -070088
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
97static 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;
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +0200103 struct device *dev = file->private_data;
104 struct omap_iommu *obj = dev_to_omap_iommu(dev);
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700105 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
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300121 omap_iotlb_cr_to_e(&cr, &e);
122 err = omap_iopgtable_store_entry(obj, &e);
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700123 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
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300144static ssize_t dump_ioptable(struct omap_iommu *obj, char *buf, ssize_t len)
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700145{
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 }
182out:
183 spin_unlock(&obj->page_table_lock);
184
185 return p - buf;
186}
187
188static ssize_t debug_read_pagetable(struct file *file, char __user *userbuf,
189 size_t count, loff_t *ppos)
190{
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +0200191 struct device *dev = file->private_data;
192 struct omap_iommu *obj = dev_to_omap_iommu(dev);
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700193 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
217static ssize_t debug_read_mmap(struct file *file, char __user *userbuf,
218 size_t count, loff_t *ppos)
219{
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +0200220 struct device *dev = file->private_data;
221 struct omap_iommu *obj = dev_to_omap_iommu(dev);
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700222 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
260static ssize_t debug_read_mem(struct file *file, char __user *userbuf,
261 size_t count, loff_t *ppos)
262{
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +0200263 struct device *dev = file->private_data;
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700264 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
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +0200277 area = omap_find_iovm_area(dev, (u32)ppos);
Ohad Ben-Cohen87997aa2012-02-22 11:14:46 +0200278 if (!area) {
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700279 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);
286err_out:
287 mutex_unlock(&iommu_debug_lock);
288 free_page((unsigned long)buf);
289
290 return bytes;
291}
292
293static ssize_t debug_write_mem(struct file *file, const char __user *userbuf,
294 size_t count, loff_t *ppos)
295{
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +0200296 struct device *dev = file->private_data;
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700297 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
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +0200314 area = omap_find_iovm_area(dev, (u32)ppos);
Ohad Ben-Cohen87997aa2012-02-22 11:14:46 +0200315 if (!area) {
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700316 count = -EINVAL;
317 goto err_out;
318 }
319 memcpy(area->va, p, count);
320err_out:
321 mutex_unlock(&iommu_debug_lock);
322 free_page((unsigned long)buf);
323
324 return count;
325}
326
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700327#define DEBUG_FOPS(name) \
328 static const struct file_operations debug_##name##_fops = { \
Stephen Boyd234e3402012-04-05 14:25:11 -0700329 .open = simple_open, \
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700330 .read = debug_read_##name, \
331 .write = debug_write_##name, \
Arnd Bergmannc0b0aca2010-07-06 19:16:33 +0200332 .llseek = generic_file_llseek, \
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700333 };
334
335#define DEBUG_FOPS_RO(name) \
336 static const struct file_operations debug_##name##_fops = { \
Stephen Boyd234e3402012-04-05 14:25:11 -0700337 .open = simple_open, \
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700338 .read = debug_read_##name, \
Arnd Bergmannc0b0aca2010-07-06 19:16:33 +0200339 .llseek = generic_file_llseek, \
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700340 };
341
342DEBUG_FOPS_RO(ver);
343DEBUG_FOPS_RO(regs);
344DEBUG_FOPS_RO(tlb);
345DEBUG_FOPS(pagetable);
346DEBUG_FOPS_RO(mmap);
347DEBUG_FOPS(mem);
348
349#define __DEBUG_ADD_FILE(attr, mode) \
350 { \
351 struct dentry *dent; \
352 dent = debugfs_create_file(#attr, mode, parent, \
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +0200353 dev, &debug_##attr##_fops); \
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700354 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
361static int iommu_debug_register(struct device *dev, void *data)
362{
363 struct platform_device *pdev = to_platform_device(dev);
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300364 struct omap_iommu *obj = platform_get_drvdata(pdev);
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +0200365 struct omap_iommu_arch_data *arch_data;
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700366 struct dentry *d, *parent;
367
368 if (!obj || !obj->dev)
369 return -EINVAL;
370
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +0200371 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
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700379 d = debugfs_create_dir(obj->name, iommu_debug_root);
380 if (!d)
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +0200381 goto nomem;
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700382 parent = d;
383
384 d = debugfs_create_u8("nr_tlb_entries", 400, parent,
385 (u8 *)&obj->nr_tlb_entries);
386 if (!d)
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +0200387 goto nomem;
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700388
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;
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +0200397
398nomem:
399 kfree(arch_data);
400 return -ENOMEM;
401}
402
403static 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;
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700413}
414
415static 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
Ohad Ben-Cohen6c32df42011-08-17 22:57:56 +0300425 err = omap_foreach_iommu_device(d, iommu_debug_register);
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700426 if (err)
427 goto err_out;
428 return 0;
429
430err_out:
431 debugfs_remove_recursive(iommu_debug_root);
432 return err;
433}
434module_init(iommu_debug_init)
435
436static void __exit iommu_debugfs_exit(void)
437{
438 debugfs_remove_recursive(iommu_debug_root);
Ohad Ben-Cohen46451d62012-02-22 10:52:51 +0200439 omap_foreach_iommu_device(NULL, iommu_debug_unregister);
Hiroshi DOYU14e0e672009-08-28 10:54:41 -0700440}
441module_exit(iommu_debugfs_exit)
442
443MODULE_DESCRIPTION("omap iommu: debugfs interface");
444MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>");
445MODULE_LICENSE("GPL v2");