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