diff options
Diffstat (limited to 'drivers/iommu/omap-iommu-debug.c')
-rw-r--r-- | drivers/iommu/omap-iommu-debug.c | 242 |
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 | |||
29 | static DEFINE_MUTEX(iommu_debug_lock); | 23 | static DEFINE_MUTEX(iommu_debug_lock); |
30 | 24 | ||
31 | static struct dentry *iommu_debug_root; | 25 | static struct dentry *iommu_debug_root; |
32 | 26 | ||
33 | static ssize_t debug_read_ver(struct file *file, char __user *userbuf, | 27 | static 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 | ||
44 | static ssize_t debug_read_regs(struct file *file, char __user *userbuf, | 32 | static 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, | |||
68 | static ssize_t debug_read_tlb(struct file *file, char __user *userbuf, | 58 | static 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 | ||
96 | static ssize_t debug_write_pagetable(struct file *file, | 88 | static 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 | |||
143 | static 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 | } |
181 | out: | ||
182 | spin_unlock(&obj->page_table_lock); | ||
183 | 117 | ||
184 | return p - buf; | 118 | spin_unlock(&obj->page_table_lock); |
185 | } | 119 | } |
186 | 120 | ||
187 | static ssize_t debug_read_pagetable(struct file *file, char __user *userbuf, | 121 | static 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 | ||
231 | DEBUG_FOPS_RO(ver); | ||
232 | DEBUG_FOPS_RO(regs); | 159 | DEBUG_FOPS_RO(regs); |
233 | DEBUG_FOPS_RO(tlb); | 160 | DEBUG_FOPS_RO(tlb); |
234 | DEBUG_FOPS(pagetable); | 161 | DEBUG_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 | ||
248 | static int iommu_debug_register(struct device *dev, void *data) | 174 | void 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 | ||
283 | nomem: | 196 | err: |
284 | kfree(arch_data); | 197 | debugfs_remove_recursive(obj->debug_dir); |
285 | return -ENOMEM; | ||
286 | } | 198 | } |
287 | 199 | ||
288 | static int iommu_debug_unregister(struct device *dev, void *data) | 200 | void 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 | ||
300 | static int __init iommu_debug_init(void) | 208 | void __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 | |||
315 | err_out: | ||
316 | debugfs_remove_recursive(iommu_debug_root); | ||
317 | return err; | ||
318 | } | 213 | } |
319 | module_init(iommu_debug_init) | ||
320 | 214 | ||
321 | static void __exit iommu_debugfs_exit(void) | 215 | void __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 | } |
326 | module_exit(iommu_debugfs_exit) | ||
327 | |||
328 | MODULE_DESCRIPTION("omap iommu: debugfs interface"); | ||
329 | MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>"); | ||
330 | MODULE_LICENSE("GPL v2"); | ||