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