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 #define DEBUG_FOPS(name) \ 327 static const struct file_operations debug_##name##_fops = { \ 328 .open = simple_open, \ 329 .read = debug_read_##name, \ 330 .write = debug_write_##name, \ 331 .llseek = generic_file_llseek, \ 332 }; 333 334 #define DEBUG_FOPS_RO(name) \ 335 static const struct file_operations debug_##name##_fops = { \ 336 .open = simple_open, \ 337 .read = debug_read_##name, \ 338 .llseek = generic_file_llseek, \ 339 }; 340 341 DEBUG_FOPS_RO(ver); 342 DEBUG_FOPS_RO(regs); 343 DEBUG_FOPS_RO(tlb); 344 DEBUG_FOPS(pagetable); 345 DEBUG_FOPS_RO(mmap); 346 DEBUG_FOPS(mem); 347 348 #define __DEBUG_ADD_FILE(attr, mode) \ 349 { \ 350 struct dentry *dent; \ 351 dent = debugfs_create_file(#attr, mode, parent, \ 352 dev, &debug_##attr##_fops); \ 353 if (!dent) \ 354 return -ENOMEM; \ 355 } 356 357 #define DEBUG_ADD_FILE(name) __DEBUG_ADD_FILE(name, 600) 358 #define DEBUG_ADD_FILE_RO(name) __DEBUG_ADD_FILE(name, 400) 359 360 static int iommu_debug_register(struct device *dev, void *data) 361 { 362 struct platform_device *pdev = to_platform_device(dev); 363 struct omap_iommu *obj = platform_get_drvdata(pdev); 364 struct omap_iommu_arch_data *arch_data; 365 struct dentry *d, *parent; 366 367 if (!obj || !obj->dev) 368 return -EINVAL; 369 370 arch_data = kzalloc(sizeof(*arch_data), GFP_KERNEL); 371 if (!arch_data) 372 return -ENOMEM; 373 374 arch_data->iommu_dev = obj; 375 376 dev->archdata.iommu = arch_data; 377 378 d = debugfs_create_dir(obj->name, iommu_debug_root); 379 if (!d) 380 goto nomem; 381 parent = d; 382 383 d = debugfs_create_u8("nr_tlb_entries", 400, parent, 384 (u8 *)&obj->nr_tlb_entries); 385 if (!d) 386 goto nomem; 387 388 DEBUG_ADD_FILE_RO(ver); 389 DEBUG_ADD_FILE_RO(regs); 390 DEBUG_ADD_FILE_RO(tlb); 391 DEBUG_ADD_FILE(pagetable); 392 DEBUG_ADD_FILE_RO(mmap); 393 DEBUG_ADD_FILE(mem); 394 395 return 0; 396 397 nomem: 398 kfree(arch_data); 399 return -ENOMEM; 400 } 401 402 static int iommu_debug_unregister(struct device *dev, void *data) 403 { 404 if (!dev->archdata.iommu) 405 return 0; 406 407 kfree(dev->archdata.iommu); 408 409 dev->archdata.iommu = NULL; 410 411 return 0; 412 } 413 414 static int __init iommu_debug_init(void) 415 { 416 struct dentry *d; 417 int err; 418 419 d = debugfs_create_dir("iommu", NULL); 420 if (!d) 421 return -ENOMEM; 422 iommu_debug_root = d; 423 424 err = omap_foreach_iommu_device(d, iommu_debug_register); 425 if (err) 426 goto err_out; 427 return 0; 428 429 err_out: 430 debugfs_remove_recursive(iommu_debug_root); 431 return err; 432 } 433 module_init(iommu_debug_init) 434 435 static void __exit iommu_debugfs_exit(void) 436 { 437 debugfs_remove_recursive(iommu_debug_root); 438 omap_foreach_iommu_device(NULL, iommu_debug_unregister); 439 } 440 module_exit(iommu_debugfs_exit) 441 442 MODULE_DESCRIPTION("omap iommu: debugfs interface"); 443 MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>"); 444 MODULE_LICENSE("GPL v2"); 445