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 #include <linux/omap-iommu.h> 22 #include <linux/platform_data/iommu-omap.h> 23 24 #include "omap-iopgtable.h" 25 #include "omap-iommu.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 #define DEBUG_FOPS(name) \ 217 static const struct file_operations debug_##name##_fops = { \ 218 .open = simple_open, \ 219 .read = debug_read_##name, \ 220 .write = debug_write_##name, \ 221 .llseek = generic_file_llseek, \ 222 }; 223 224 #define DEBUG_FOPS_RO(name) \ 225 static const struct file_operations debug_##name##_fops = { \ 226 .open = simple_open, \ 227 .read = debug_read_##name, \ 228 .llseek = generic_file_llseek, \ 229 }; 230 231 DEBUG_FOPS_RO(ver); 232 DEBUG_FOPS_RO(regs); 233 DEBUG_FOPS_RO(tlb); 234 DEBUG_FOPS(pagetable); 235 236 #define __DEBUG_ADD_FILE(attr, mode) \ 237 { \ 238 struct dentry *dent; \ 239 dent = debugfs_create_file(#attr, mode, parent, \ 240 dev, &debug_##attr##_fops); \ 241 if (!dent) \ 242 return -ENOMEM; \ 243 } 244 245 #define DEBUG_ADD_FILE(name) __DEBUG_ADD_FILE(name, 0600) 246 #define DEBUG_ADD_FILE_RO(name) __DEBUG_ADD_FILE(name, 0400) 247 248 static int iommu_debug_register(struct device *dev, void *data) 249 { 250 struct platform_device *pdev = to_platform_device(dev); 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 264 dev->archdata.iommu = arch_data; 265 266 d = debugfs_create_dir(obj->name, iommu_debug_root); 267 if (!d) 268 goto nomem; 269 parent = d; 270 271 d = debugfs_create_u8("nr_tlb_entries", 400, parent, 272 (u8 *)&obj->nr_tlb_entries); 273 if (!d) 274 goto nomem; 275 276 DEBUG_ADD_FILE_RO(ver); 277 DEBUG_ADD_FILE_RO(regs); 278 DEBUG_ADD_FILE_RO(tlb); 279 DEBUG_ADD_FILE(pagetable); 280 281 return 0; 282 283 nomem: 284 kfree(arch_data); 285 return -ENOMEM; 286 } 287 288 static int iommu_debug_unregister(struct device *dev, void *data) 289 { 290 if (!dev->archdata.iommu) 291 return 0; 292 293 kfree(dev->archdata.iommu); 294 295 dev->archdata.iommu = NULL; 296 297 return 0; 298 } 299 300 static int __init iommu_debug_init(void) 301 { 302 struct dentry *d; 303 int err; 304 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 } 319 module_init(iommu_debug_init) 320 321 static void __exit iommu_debugfs_exit(void) 322 { 323 debugfs_remove_recursive(iommu_debug_root); 324 omap_foreach_iommu_device(NULL, iommu_debug_unregister); 325 } 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"); 331