aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iommu/omap-iommu-debug.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iommu/omap-iommu-debug.c')
-rw-r--r--drivers/iommu/omap-iommu-debug.c242
1 files changed, 65 insertions, 177 deletions
diff --git a/drivers/iommu/omap-iommu-debug.c b/drivers/iommu/omap-iommu-debug.c
index 531658d17333..f3d20a2039d2 100644
--- a/drivers/iommu/omap-iommu-debug.c
+++ b/drivers/iommu/omap-iommu-debug.c
@@ -10,45 +10,35 @@
10 * published by the Free Software Foundation. 10 * published by the Free Software Foundation.
11 */ 11 */
12 12
13#include <linux/module.h>
14#include <linux/err.h> 13#include <linux/err.h>
15#include <linux/clk.h>
16#include <linux/io.h> 14#include <linux/io.h>
17#include <linux/slab.h> 15#include <linux/slab.h>
18#include <linux/uaccess.h> 16#include <linux/uaccess.h>
19#include <linux/platform_device.h>
20#include <linux/debugfs.h> 17#include <linux/debugfs.h>
21#include <linux/omap-iommu.h>
22#include <linux/platform_data/iommu-omap.h> 18#include <linux/platform_data/iommu-omap.h>
23 19
24#include "omap-iopgtable.h" 20#include "omap-iopgtable.h"
25#include "omap-iommu.h" 21#include "omap-iommu.h"
26 22
27#define MAXCOLUMN 100 /* for short messages */
28
29static DEFINE_MUTEX(iommu_debug_lock); 23static DEFINE_MUTEX(iommu_debug_lock);
30 24
31static struct dentry *iommu_debug_root; 25static struct dentry *iommu_debug_root;
32 26
33static ssize_t debug_read_ver(struct file *file, char __user *userbuf, 27static inline bool is_omap_iommu_detached(struct omap_iommu *obj)
34 size_t count, loff_t *ppos)
35{ 28{
36 u32 ver = omap_iommu_arch_version(); 29 return !obj->domain;
37 char buf[MAXCOLUMN], *p = buf;
38
39 p += sprintf(p, "H/W version: %d.%d\n", (ver >> 4) & 0xf , ver & 0xf);
40
41 return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
42} 30}
43 31
44static ssize_t debug_read_regs(struct file *file, char __user *userbuf, 32static ssize_t debug_read_regs(struct file *file, char __user *userbuf,
45 size_t count, loff_t *ppos) 33 size_t count, loff_t *ppos)
46{ 34{
47 struct device *dev = file->private_data; 35 struct omap_iommu *obj = file->private_data;
48 struct omap_iommu *obj = dev_to_omap_iommu(dev);
49 char *p, *buf; 36 char *p, *buf;
50 ssize_t bytes; 37 ssize_t bytes;
51 38
39 if (is_omap_iommu_detached(obj))
40 return -EPERM;
41
52 buf = kmalloc(count, GFP_KERNEL); 42 buf = kmalloc(count, GFP_KERNEL);
53 if (!buf) 43 if (!buf)
54 return -ENOMEM; 44 return -ENOMEM;
@@ -68,11 +58,13 @@ static ssize_t debug_read_regs(struct file *file, char __user *userbuf,
68static ssize_t debug_read_tlb(struct file *file, char __user *userbuf, 58static ssize_t debug_read_tlb(struct file *file, char __user *userbuf,
69 size_t count, loff_t *ppos) 59 size_t count, loff_t *ppos)
70{ 60{
71 struct device *dev = file->private_data; 61 struct omap_iommu *obj = file->private_data;
72 struct omap_iommu *obj = dev_to_omap_iommu(dev);
73 char *p, *buf; 62 char *p, *buf;
74 ssize_t bytes, rest; 63 ssize_t bytes, rest;
75 64
65 if (is_omap_iommu_detached(obj))
66 return -EPERM;
67
76 buf = kmalloc(count, GFP_KERNEL); 68 buf = kmalloc(count, GFP_KERNEL);
77 if (!buf) 69 if (!buf)
78 return -ENOMEM; 70 return -ENOMEM;
@@ -93,133 +85,69 @@ static ssize_t debug_read_tlb(struct file *file, char __user *userbuf,
93 return bytes; 85 return bytes;
94} 86}
95 87
96static ssize_t debug_write_pagetable(struct file *file, 88static void dump_ioptable(struct seq_file *s)
97 const char __user *userbuf, size_t count, loff_t *ppos)
98{ 89{
99 struct iotlb_entry e; 90 int i, j;
100 struct cr_regs cr; 91 u32 da;
101 int err; 92 u32 *iopgd, *iopte;
102 struct device *dev = file->private_data; 93 struct omap_iommu *obj = s->private;
103 struct omap_iommu *obj = dev_to_omap_iommu(dev);
104 char buf[MAXCOLUMN], *p = buf;
105
106 count = min(count, sizeof(buf));
107
108 mutex_lock(&iommu_debug_lock);
109 if (copy_from_user(p, userbuf, count)) {
110 mutex_unlock(&iommu_debug_lock);
111 return -EFAULT;
112 }
113
114 sscanf(p, "%x %x", &cr.cam, &cr.ram);
115 if (!cr.cam || !cr.ram) {
116 mutex_unlock(&iommu_debug_lock);
117 return -EINVAL;
118 }
119
120 omap_iotlb_cr_to_e(&cr, &e);
121 err = omap_iopgtable_store_entry(obj, &e);
122 if (err)
123 dev_err(obj->dev, "%s: fail to store cr\n", __func__);
124
125 mutex_unlock(&iommu_debug_lock);
126 return count;
127}
128
129#define dump_ioptable_entry_one(lv, da, val) \
130 ({ \
131 int __err = 0; \
132 ssize_t bytes; \
133 const int maxcol = 22; \
134 const char *str = "%d: %08x %08x\n"; \
135 bytes = snprintf(p, maxcol, str, lv, da, val); \
136 p += bytes; \
137 len -= bytes; \
138 if (len < maxcol) \
139 __err = -ENOMEM; \
140 __err; \
141 })
142
143static ssize_t dump_ioptable(struct omap_iommu *obj, char *buf, ssize_t len)
144{
145 int i;
146 u32 *iopgd;
147 char *p = buf;
148 94
149 spin_lock(&obj->page_table_lock); 95 spin_lock(&obj->page_table_lock);
150 96
151 iopgd = iopgd_offset(obj, 0); 97 iopgd = iopgd_offset(obj, 0);
152 for (i = 0; i < PTRS_PER_IOPGD; i++, iopgd++) { 98 for (i = 0; i < PTRS_PER_IOPGD; i++, iopgd++) {
153 int j, err;
154 u32 *iopte;
155 u32 da;
156
157 if (!*iopgd) 99 if (!*iopgd)
158 continue; 100 continue;
159 101
160 if (!(*iopgd & IOPGD_TABLE)) { 102 if (!(*iopgd & IOPGD_TABLE)) {
161 da = i << IOPGD_SHIFT; 103 da = i << IOPGD_SHIFT;
162 104 seq_printf(s, "1: 0x%08x 0x%08x\n", da, *iopgd);
163 err = dump_ioptable_entry_one(1, da, *iopgd);
164 if (err)
165 goto out;
166 continue; 105 continue;
167 } 106 }
168 107
169 iopte = iopte_offset(iopgd, 0); 108 iopte = iopte_offset(iopgd, 0);
170
171 for (j = 0; j < PTRS_PER_IOPTE; j++, iopte++) { 109 for (j = 0; j < PTRS_PER_IOPTE; j++, iopte++) {
172 if (!*iopte) 110 if (!*iopte)
173 continue; 111 continue;
174 112
175 da = (i << IOPGD_SHIFT) + (j << IOPTE_SHIFT); 113 da = (i << IOPGD_SHIFT) + (j << IOPTE_SHIFT);
176 err = dump_ioptable_entry_one(2, da, *iopgd); 114 seq_printf(s, "2: 0x%08x 0x%08x\n", da, *iopte);
177 if (err)
178 goto out;
179 } 115 }
180 } 116 }
181out:
182 spin_unlock(&obj->page_table_lock);
183 117
184 return p - buf; 118 spin_unlock(&obj->page_table_lock);
185} 119}
186 120
187static ssize_t debug_read_pagetable(struct file *file, char __user *userbuf, 121static int debug_read_pagetable(struct seq_file *s, void *data)
188 size_t count, loff_t *ppos)
189{ 122{
190 struct device *dev = file->private_data; 123 struct omap_iommu *obj = s->private;
191 struct omap_iommu *obj = dev_to_omap_iommu(dev);
192 char *p, *buf;
193 size_t bytes;
194 124
195 buf = (char *)__get_free_page(GFP_KERNEL); 125 if (is_omap_iommu_detached(obj))
196 if (!buf) 126 return -EPERM;
197 return -ENOMEM;
198 p = buf;
199
200 p += sprintf(p, "L: %8s %8s\n", "da:", "pa:");
201 p += sprintf(p, "-----------------------------------------\n");
202 127
203 mutex_lock(&iommu_debug_lock); 128 mutex_lock(&iommu_debug_lock);
204 129
205 bytes = PAGE_SIZE - (p - buf); 130 seq_printf(s, "L: %8s %8s\n", "da:", "pte:");
206 p += dump_ioptable(obj, p, bytes); 131 seq_puts(s, "--------------------------\n");
207 132 dump_ioptable(s);
208 bytes = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
209 133
210 mutex_unlock(&iommu_debug_lock); 134 mutex_unlock(&iommu_debug_lock);
211 free_page((unsigned long)buf);
212 135
213 return bytes; 136 return 0;
214} 137}
215 138
216#define DEBUG_FOPS(name) \ 139#define DEBUG_SEQ_FOPS_RO(name) \
217 static const struct file_operations debug_##name##_fops = { \ 140 static int debug_open_##name(struct inode *inode, struct file *file) \
218 .open = simple_open, \ 141 { \
219 .read = debug_read_##name, \ 142 return single_open(file, debug_read_##name, inode->i_private); \
220 .write = debug_write_##name, \ 143 } \
221 .llseek = generic_file_llseek, \ 144 \
222 }; 145 static const struct file_operations debug_##name##_fops = { \
146 .open = debug_open_##name, \
147 .read = seq_read, \
148 .llseek = seq_lseek, \
149 .release = single_release, \
150 }
223 151
224#define DEBUG_FOPS_RO(name) \ 152#define DEBUG_FOPS_RO(name) \
225 static const struct file_operations debug_##name##_fops = { \ 153 static const struct file_operations debug_##name##_fops = { \
@@ -228,103 +156,63 @@ static ssize_t debug_read_pagetable(struct file *file, char __user *userbuf,
228 .llseek = generic_file_llseek, \ 156 .llseek = generic_file_llseek, \
229 }; 157 };
230 158
231DEBUG_FOPS_RO(ver);
232DEBUG_FOPS_RO(regs); 159DEBUG_FOPS_RO(regs);
233DEBUG_FOPS_RO(tlb); 160DEBUG_FOPS_RO(tlb);
234DEBUG_FOPS(pagetable); 161DEBUG_SEQ_FOPS_RO(pagetable);
235 162
236#define __DEBUG_ADD_FILE(attr, mode) \ 163#define __DEBUG_ADD_FILE(attr, mode) \
237 { \ 164 { \
238 struct dentry *dent; \ 165 struct dentry *dent; \
239 dent = debugfs_create_file(#attr, mode, parent, \ 166 dent = debugfs_create_file(#attr, mode, obj->debug_dir, \
240 dev, &debug_##attr##_fops); \ 167 obj, &debug_##attr##_fops); \
241 if (!dent) \ 168 if (!dent) \
242 return -ENOMEM; \ 169 goto err; \
243 } 170 }
244 171
245#define DEBUG_ADD_FILE(name) __DEBUG_ADD_FILE(name, 0600)
246#define DEBUG_ADD_FILE_RO(name) __DEBUG_ADD_FILE(name, 0400) 172#define DEBUG_ADD_FILE_RO(name) __DEBUG_ADD_FILE(name, 0400)
247 173
248static int iommu_debug_register(struct device *dev, void *data) 174void omap_iommu_debugfs_add(struct omap_iommu *obj)
249{ 175{
250 struct platform_device *pdev = to_platform_device(dev); 176 struct dentry *d;
251 struct omap_iommu *obj = platform_get_drvdata(pdev);
252 struct omap_iommu_arch_data *arch_data;
253 struct dentry *d, *parent;
254
255 if (!obj || !obj->dev)
256 return -EINVAL;
257
258 arch_data = kzalloc(sizeof(*arch_data), GFP_KERNEL);
259 if (!arch_data)
260 return -ENOMEM;
261
262 arch_data->iommu_dev = obj;
263 177
264 dev->archdata.iommu = arch_data; 178 if (!iommu_debug_root)
179 return;
265 180
266 d = debugfs_create_dir(obj->name, iommu_debug_root); 181 obj->debug_dir = debugfs_create_dir(obj->name, iommu_debug_root);
267 if (!d) 182 if (!obj->debug_dir)
268 goto nomem; 183 return;
269 parent = d;
270 184
271 d = debugfs_create_u8("nr_tlb_entries", 400, parent, 185 d = debugfs_create_u8("nr_tlb_entries", 0400, obj->debug_dir,
272 (u8 *)&obj->nr_tlb_entries); 186 (u8 *)&obj->nr_tlb_entries);
273 if (!d) 187 if (!d)
274 goto nomem; 188 return;
275 189
276 DEBUG_ADD_FILE_RO(ver);
277 DEBUG_ADD_FILE_RO(regs); 190 DEBUG_ADD_FILE_RO(regs);
278 DEBUG_ADD_FILE_RO(tlb); 191 DEBUG_ADD_FILE_RO(tlb);
279 DEBUG_ADD_FILE(pagetable); 192 DEBUG_ADD_FILE_RO(pagetable);
280 193
281 return 0; 194 return;
282 195
283nomem: 196err:
284 kfree(arch_data); 197 debugfs_remove_recursive(obj->debug_dir);
285 return -ENOMEM;
286} 198}
287 199
288static int iommu_debug_unregister(struct device *dev, void *data) 200void omap_iommu_debugfs_remove(struct omap_iommu *obj)
289{ 201{
290 if (!dev->archdata.iommu) 202 if (!obj->debug_dir)
291 return 0; 203 return;
292
293 kfree(dev->archdata.iommu);
294 204
295 dev->archdata.iommu = NULL; 205 debugfs_remove_recursive(obj->debug_dir);
296
297 return 0;
298} 206}
299 207
300static int __init iommu_debug_init(void) 208void __init omap_iommu_debugfs_init(void)
301{ 209{
302 struct dentry *d; 210 iommu_debug_root = debugfs_create_dir("omap_iommu", NULL);
303 int err; 211 if (!iommu_debug_root)
304 212 pr_err("can't create debugfs dir\n");
305 d = debugfs_create_dir("iommu", NULL);
306 if (!d)
307 return -ENOMEM;
308 iommu_debug_root = d;
309
310 err = omap_foreach_iommu_device(d, iommu_debug_register);
311 if (err)
312 goto err_out;
313 return 0;
314
315err_out:
316 debugfs_remove_recursive(iommu_debug_root);
317 return err;
318} 213}
319module_init(iommu_debug_init)
320 214
321static void __exit iommu_debugfs_exit(void) 215void __exit omap_iommu_debugfs_exit(void)
322{ 216{
323 debugfs_remove_recursive(iommu_debug_root); 217 debugfs_remove(iommu_debug_root);
324 omap_foreach_iommu_device(NULL, iommu_debug_unregister);
325} 218}
326module_exit(iommu_debugfs_exit)
327
328MODULE_DESCRIPTION("omap iommu: debugfs interface");
329MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>");
330MODULE_LICENSE("GPL v2");