131244e39SMark Brown /* 231244e39SMark Brown * Register map access API - debugfs 331244e39SMark Brown * 431244e39SMark Brown * Copyright 2011 Wolfson Microelectronics plc 531244e39SMark Brown * 631244e39SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 731244e39SMark Brown * 831244e39SMark Brown * This program is free software; you can redistribute it and/or modify 931244e39SMark Brown * it under the terms of the GNU General Public License version 2 as 1031244e39SMark Brown * published by the Free Software Foundation. 1131244e39SMark Brown */ 1231244e39SMark Brown 1331244e39SMark Brown #include <linux/slab.h> 1431244e39SMark Brown #include <linux/mutex.h> 1531244e39SMark Brown #include <linux/debugfs.h> 1631244e39SMark Brown #include <linux/uaccess.h> 1751990e82SPaul Gortmaker #include <linux/device.h> 18a52eaeb1STero Kristo #include <linux/list.h> 1931244e39SMark Brown 2031244e39SMark Brown #include "internal.h" 2131244e39SMark Brown 22a52eaeb1STero Kristo struct regmap_debugfs_node { 23a52eaeb1STero Kristo struct regmap *map; 24a52eaeb1STero Kristo const char *name; 25a52eaeb1STero Kristo struct list_head link; 26a52eaeb1STero Kristo }; 27a52eaeb1STero Kristo 28a430ab20SFabio Estevam static unsigned int dummy_index; 2931244e39SMark Brown static struct dentry *regmap_debugfs_root; 30a52eaeb1STero Kristo static LIST_HEAD(regmap_debugfs_early_list); 31a52eaeb1STero Kristo static DEFINE_MUTEX(regmap_debugfs_early_lock); 3231244e39SMark Brown 3321f55544SMark Brown /* Calculate the length of a fixed format */ 349ae3109dSMark Brown static size_t regmap_calc_reg_len(int max_val) 3521f55544SMark Brown { 36176fc2d5SMark Brown return snprintf(NULL, 0, "%x", max_val); 3721f55544SMark Brown } 3821f55544SMark Brown 39f0c2319fSDimitris Papastamos static ssize_t regmap_name_read_file(struct file *file, 40f0c2319fSDimitris Papastamos char __user *user_buf, size_t count, 41f0c2319fSDimitris Papastamos loff_t *ppos) 42f0c2319fSDimitris Papastamos { 43f0c2319fSDimitris Papastamos struct regmap *map = file->private_data; 4412ae3808SDavid Lechner const char *name = "nodev"; 45f0c2319fSDimitris Papastamos int ret; 46f0c2319fSDimitris Papastamos char *buf; 47f0c2319fSDimitris Papastamos 48f0c2319fSDimitris Papastamos buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 49f0c2319fSDimitris Papastamos if (!buf) 50f0c2319fSDimitris Papastamos return -ENOMEM; 51f0c2319fSDimitris Papastamos 5212ae3808SDavid Lechner if (map->dev && map->dev->driver) 5312ae3808SDavid Lechner name = map->dev->driver->name; 5412ae3808SDavid Lechner 5512ae3808SDavid Lechner ret = snprintf(buf, PAGE_SIZE, "%s\n", name); 56f0c2319fSDimitris Papastamos if (ret < 0) { 57f0c2319fSDimitris Papastamos kfree(buf); 58f0c2319fSDimitris Papastamos return ret; 59f0c2319fSDimitris Papastamos } 60f0c2319fSDimitris Papastamos 61f0c2319fSDimitris Papastamos ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); 62f0c2319fSDimitris Papastamos kfree(buf); 63f0c2319fSDimitris Papastamos return ret; 64f0c2319fSDimitris Papastamos } 65f0c2319fSDimitris Papastamos 66f0c2319fSDimitris Papastamos static const struct file_operations regmap_name_fops = { 67234e3405SStephen Boyd .open = simple_open, 68f0c2319fSDimitris Papastamos .read = regmap_name_read_file, 69f0c2319fSDimitris Papastamos .llseek = default_llseek, 70f0c2319fSDimitris Papastamos }; 71f0c2319fSDimitris Papastamos 7295f971c7SMark Brown static void regmap_debugfs_free_dump_cache(struct regmap *map) 7395f971c7SMark Brown { 7495f971c7SMark Brown struct regmap_debugfs_off_cache *c; 7595f971c7SMark Brown 7695f971c7SMark Brown while (!list_empty(&map->debugfs_off_cache)) { 7795f971c7SMark Brown c = list_first_entry(&map->debugfs_off_cache, 7895f971c7SMark Brown struct regmap_debugfs_off_cache, 7995f971c7SMark Brown list); 8095f971c7SMark Brown list_del(&c->list); 8195f971c7SMark Brown kfree(c); 8295f971c7SMark Brown } 8395f971c7SMark Brown } 8495f971c7SMark Brown 85359a2f17SCristian Birsan static bool regmap_printable(struct regmap *map, unsigned int reg) 86359a2f17SCristian Birsan { 87359a2f17SCristian Birsan if (regmap_precious(map, reg)) 88359a2f17SCristian Birsan return false; 89359a2f17SCristian Birsan 90359a2f17SCristian Birsan if (!regmap_readable(map, reg) && !regmap_cached(map, reg)) 91359a2f17SCristian Birsan return false; 92359a2f17SCristian Birsan 93359a2f17SCristian Birsan return true; 94359a2f17SCristian Birsan } 95359a2f17SCristian Birsan 96afab2f7bSMark Brown /* 97afab2f7bSMark Brown * Work out where the start offset maps into register numbers, bearing 98afab2f7bSMark Brown * in mind that we suppress hidden registers. 99afab2f7bSMark Brown */ 100afab2f7bSMark Brown static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, 101afab2f7bSMark Brown unsigned int base, 102afab2f7bSMark Brown loff_t from, 103afab2f7bSMark Brown loff_t *pos) 104afab2f7bSMark Brown { 1055166b7c0SMark Brown struct regmap_debugfs_off_cache *c = NULL; 1065166b7c0SMark Brown loff_t p = 0; 1075166b7c0SMark Brown unsigned int i, ret; 108c2c1ee66SDimitris Papastamos unsigned int fpos_offset; 109c2c1ee66SDimitris Papastamos unsigned int reg_offset; 110afab2f7bSMark Brown 111d6814a7dSMark Brown /* Suppress the cache if we're using a subrange */ 11226ee4741SLars-Peter Clausen if (base) 11326ee4741SLars-Peter Clausen return base; 114d6814a7dSMark Brown 1155166b7c0SMark Brown /* 1165166b7c0SMark Brown * If we don't have a cache build one so we don't have to do a 1175166b7c0SMark Brown * linear scan each time. 1185166b7c0SMark Brown */ 119065b4c58SDimitris Papastamos mutex_lock(&map->cache_lock); 120480738deSDimitris Papastamos i = base; 1215166b7c0SMark Brown if (list_empty(&map->debugfs_off_cache)) { 122480738deSDimitris Papastamos for (; i <= map->max_register; i += map->reg_stride) { 1235166b7c0SMark Brown /* Skip unprinted registers, closing off cache entry */ 124359a2f17SCristian Birsan if (!regmap_printable(map, i)) { 1255166b7c0SMark Brown if (c) { 1265166b7c0SMark Brown c->max = p - 1; 127480738deSDimitris Papastamos c->max_reg = i - map->reg_stride; 1285166b7c0SMark Brown list_add_tail(&c->list, 1295166b7c0SMark Brown &map->debugfs_off_cache); 1305166b7c0SMark Brown c = NULL; 1315166b7c0SMark Brown } 132afab2f7bSMark Brown 133afab2f7bSMark Brown continue; 1345166b7c0SMark Brown } 135afab2f7bSMark Brown 1365166b7c0SMark Brown /* No cache entry? Start a new one */ 1375166b7c0SMark Brown if (!c) { 1385166b7c0SMark Brown c = kzalloc(sizeof(*c), GFP_KERNEL); 13995f971c7SMark Brown if (!c) { 14095f971c7SMark Brown regmap_debugfs_free_dump_cache(map); 141065b4c58SDimitris Papastamos mutex_unlock(&map->cache_lock); 14295f971c7SMark Brown return base; 14395f971c7SMark Brown } 1445166b7c0SMark Brown c->min = p; 1455166b7c0SMark Brown c->base_reg = i; 146afab2f7bSMark Brown } 147afab2f7bSMark Brown 148afab2f7bSMark Brown p += map->debugfs_tot_len; 149afab2f7bSMark Brown } 1505166b7c0SMark Brown } 151afab2f7bSMark Brown 152e8d6539cSMark Brown /* Close the last entry off if we didn't scan beyond it */ 153e8d6539cSMark Brown if (c) { 154e8d6539cSMark Brown c->max = p - 1; 155480738deSDimitris Papastamos c->max_reg = i - map->reg_stride; 156e8d6539cSMark Brown list_add_tail(&c->list, 157e8d6539cSMark Brown &map->debugfs_off_cache); 158e8d6539cSMark Brown } 159e8d6539cSMark Brown 1605bd9f4bbSMark Brown /* 1615bd9f4bbSMark Brown * This should never happen; we return above if we fail to 1625bd9f4bbSMark Brown * allocate and we should never be in this code if there are 1635bd9f4bbSMark Brown * no registers at all. 1645bd9f4bbSMark Brown */ 1655bd9f4bbSMark Brown WARN_ON(list_empty(&map->debugfs_off_cache)); 166a3471469SRussell King ret = base; 1675bd9f4bbSMark Brown 168cf57d607SDimitris Papastamos /* Find the relevant block:offset */ 1695166b7c0SMark Brown list_for_each_entry(c, &map->debugfs_off_cache, list) { 1705a1d6d17SMark Brown if (from >= c->min && from <= c->max) { 171cf57d607SDimitris Papastamos fpos_offset = from - c->min; 172cf57d607SDimitris Papastamos reg_offset = fpos_offset / map->debugfs_tot_len; 173cf57d607SDimitris Papastamos *pos = c->min + (reg_offset * map->debugfs_tot_len); 174065b4c58SDimitris Papastamos mutex_unlock(&map->cache_lock); 175213fa5d9SSrinivas Kandagatla return c->base_reg + (reg_offset * map->reg_stride); 1765166b7c0SMark Brown } 1775166b7c0SMark Brown 178cf57d607SDimitris Papastamos *pos = c->max; 179cf57d607SDimitris Papastamos ret = c->max_reg; 1805166b7c0SMark Brown } 181065b4c58SDimitris Papastamos mutex_unlock(&map->cache_lock); 1825166b7c0SMark Brown 1835166b7c0SMark Brown return ret; 184afab2f7bSMark Brown } 185afab2f7bSMark Brown 1864dd7c553SDimitris Papastamos static inline void regmap_calc_tot_len(struct regmap *map, 1874dd7c553SDimitris Papastamos void *buf, size_t count) 1884dd7c553SDimitris Papastamos { 1894dd7c553SDimitris Papastamos /* Calculate the length of a fixed format */ 1904dd7c553SDimitris Papastamos if (!map->debugfs_tot_len) { 1919ae3109dSMark Brown map->debugfs_reg_len = regmap_calc_reg_len(map->max_register), 1924dd7c553SDimitris Papastamos map->debugfs_val_len = 2 * map->format.val_bytes; 1934dd7c553SDimitris Papastamos map->debugfs_tot_len = map->debugfs_reg_len + 1944dd7c553SDimitris Papastamos map->debugfs_val_len + 3; /* : \n */ 1954dd7c553SDimitris Papastamos } 1964dd7c553SDimitris Papastamos } 1974dd7c553SDimitris Papastamos 198bd9cc12fSMark Brown static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, 199bd9cc12fSMark Brown unsigned int to, char __user *user_buf, 20031244e39SMark Brown size_t count, loff_t *ppos) 20131244e39SMark Brown { 20231244e39SMark Brown size_t buf_pos = 0; 203afab2f7bSMark Brown loff_t p = *ppos; 20431244e39SMark Brown ssize_t ret; 20531244e39SMark Brown int i; 20631244e39SMark Brown char *buf; 207afab2f7bSMark Brown unsigned int val, start_reg; 20831244e39SMark Brown 20931244e39SMark Brown if (*ppos < 0 || !count) 21031244e39SMark Brown return -EINVAL; 21131244e39SMark Brown 21231244e39SMark Brown buf = kmalloc(count, GFP_KERNEL); 21331244e39SMark Brown if (!buf) 21431244e39SMark Brown return -ENOMEM; 21531244e39SMark Brown 2164dd7c553SDimitris Papastamos regmap_calc_tot_len(map, buf, count); 21731244e39SMark Brown 218afab2f7bSMark Brown /* Work out which register we're starting at */ 219afab2f7bSMark Brown start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p); 220afab2f7bSMark Brown 221afab2f7bSMark Brown for (i = start_reg; i <= to; i += map->reg_stride) { 222359a2f17SCristian Birsan if (!regmap_readable(map, i) && !regmap_cached(map, i)) 22331244e39SMark Brown continue; 22431244e39SMark Brown 2258de2f081SMark Brown if (regmap_precious(map, i)) 2262efe1642SMark Brown continue; 2272efe1642SMark Brown 22831244e39SMark Brown /* If we're in the region the user is trying to read */ 22931244e39SMark Brown if (p >= *ppos) { 23031244e39SMark Brown /* ...but not beyond it */ 231f3eb8399SDimitris Papastamos if (buf_pos + map->debugfs_tot_len > count) 23231244e39SMark Brown break; 23331244e39SMark Brown 23431244e39SMark Brown /* Format the register */ 23531244e39SMark Brown snprintf(buf + buf_pos, count - buf_pos, "%.*x: ", 236cbc1938bSMark Brown map->debugfs_reg_len, i - from); 237cbc1938bSMark Brown buf_pos += map->debugfs_reg_len + 2; 23831244e39SMark Brown 23931244e39SMark Brown /* Format the value, write all X if we can't read */ 24031244e39SMark Brown ret = regmap_read(map, i, &val); 24131244e39SMark Brown if (ret == 0) 24231244e39SMark Brown snprintf(buf + buf_pos, count - buf_pos, 243cbc1938bSMark Brown "%.*x", map->debugfs_val_len, val); 24431244e39SMark Brown else 245cbc1938bSMark Brown memset(buf + buf_pos, 'X', 246cbc1938bSMark Brown map->debugfs_val_len); 24731244e39SMark Brown buf_pos += 2 * map->format.val_bytes; 24831244e39SMark Brown 24931244e39SMark Brown buf[buf_pos++] = '\n'; 25031244e39SMark Brown } 251cbc1938bSMark Brown p += map->debugfs_tot_len; 25231244e39SMark Brown } 25331244e39SMark Brown 25431244e39SMark Brown ret = buf_pos; 25531244e39SMark Brown 25631244e39SMark Brown if (copy_to_user(user_buf, buf, buf_pos)) { 25731244e39SMark Brown ret = -EFAULT; 25831244e39SMark Brown goto out; 25931244e39SMark Brown } 26031244e39SMark Brown 26131244e39SMark Brown *ppos += buf_pos; 26231244e39SMark Brown 26331244e39SMark Brown out: 26431244e39SMark Brown kfree(buf); 26531244e39SMark Brown return ret; 26631244e39SMark Brown } 26731244e39SMark Brown 268bd9cc12fSMark Brown static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, 269bd9cc12fSMark Brown size_t count, loff_t *ppos) 270bd9cc12fSMark Brown { 271bd9cc12fSMark Brown struct regmap *map = file->private_data; 272bd9cc12fSMark Brown 273bd9cc12fSMark Brown return regmap_read_debugfs(map, 0, map->max_register, user_buf, 274bd9cc12fSMark Brown count, ppos); 275bd9cc12fSMark Brown } 276bd9cc12fSMark Brown 27709c6ecd3SDimitris Papastamos #undef REGMAP_ALLOW_WRITE_DEBUGFS 27809c6ecd3SDimitris Papastamos #ifdef REGMAP_ALLOW_WRITE_DEBUGFS 27909c6ecd3SDimitris Papastamos /* 28009c6ecd3SDimitris Papastamos * This can be dangerous especially when we have clients such as 28109c6ecd3SDimitris Papastamos * PMICs, therefore don't provide any real compile time configuration option 28209c6ecd3SDimitris Papastamos * for this feature, people who want to use this will need to modify 28309c6ecd3SDimitris Papastamos * the source code directly. 28409c6ecd3SDimitris Papastamos */ 28509c6ecd3SDimitris Papastamos static ssize_t regmap_map_write_file(struct file *file, 28609c6ecd3SDimitris Papastamos const char __user *user_buf, 28709c6ecd3SDimitris Papastamos size_t count, loff_t *ppos) 28809c6ecd3SDimitris Papastamos { 28909c6ecd3SDimitris Papastamos char buf[32]; 29009c6ecd3SDimitris Papastamos size_t buf_size; 29109c6ecd3SDimitris Papastamos char *start = buf; 29209c6ecd3SDimitris Papastamos unsigned long reg, value; 29309c6ecd3SDimitris Papastamos struct regmap *map = file->private_data; 29468e850d8SDimitris Papastamos int ret; 29509c6ecd3SDimitris Papastamos 29609c6ecd3SDimitris Papastamos buf_size = min(count, (sizeof(buf)-1)); 29709c6ecd3SDimitris Papastamos if (copy_from_user(buf, user_buf, buf_size)) 29809c6ecd3SDimitris Papastamos return -EFAULT; 29909c6ecd3SDimitris Papastamos buf[buf_size] = 0; 30009c6ecd3SDimitris Papastamos 30109c6ecd3SDimitris Papastamos while (*start == ' ') 30209c6ecd3SDimitris Papastamos start++; 30309c6ecd3SDimitris Papastamos reg = simple_strtoul(start, &start, 16); 30409c6ecd3SDimitris Papastamos while (*start == ' ') 30509c6ecd3SDimitris Papastamos start++; 30634da5e67SJingoo Han if (kstrtoul(start, 16, &value)) 30709c6ecd3SDimitris Papastamos return -EINVAL; 30809c6ecd3SDimitris Papastamos 30909c6ecd3SDimitris Papastamos /* Userspace has been fiddling around behind the kernel's back */ 310f9e464a5SMark Brown add_taint(TAINT_USER, LOCKDEP_STILL_OK); 31109c6ecd3SDimitris Papastamos 31268e850d8SDimitris Papastamos ret = regmap_write(map, reg, value); 31368e850d8SDimitris Papastamos if (ret < 0) 31468e850d8SDimitris Papastamos return ret; 31509c6ecd3SDimitris Papastamos return buf_size; 31609c6ecd3SDimitris Papastamos } 31709c6ecd3SDimitris Papastamos #else 31809c6ecd3SDimitris Papastamos #define regmap_map_write_file NULL 31909c6ecd3SDimitris Papastamos #endif 32009c6ecd3SDimitris Papastamos 32131244e39SMark Brown static const struct file_operations regmap_map_fops = { 322234e3405SStephen Boyd .open = simple_open, 32331244e39SMark Brown .read = regmap_map_read_file, 32409c6ecd3SDimitris Papastamos .write = regmap_map_write_file, 32531244e39SMark Brown .llseek = default_llseek, 32631244e39SMark Brown }; 32731244e39SMark Brown 3284b020b3fSMark Brown static ssize_t regmap_range_read_file(struct file *file, char __user *user_buf, 3294b020b3fSMark Brown size_t count, loff_t *ppos) 3304b020b3fSMark Brown { 3314b020b3fSMark Brown struct regmap_range_node *range = file->private_data; 3324b020b3fSMark Brown struct regmap *map = range->map; 3334b020b3fSMark Brown 3344b020b3fSMark Brown return regmap_read_debugfs(map, range->range_min, range->range_max, 3354b020b3fSMark Brown user_buf, count, ppos); 3364b020b3fSMark Brown } 3374b020b3fSMark Brown 3384b020b3fSMark Brown static const struct file_operations regmap_range_fops = { 3394b020b3fSMark Brown .open = simple_open, 3404b020b3fSMark Brown .read = regmap_range_read_file, 3414b020b3fSMark Brown .llseek = default_llseek, 3424b020b3fSMark Brown }; 3434b020b3fSMark Brown 344065b4c58SDimitris Papastamos static ssize_t regmap_reg_ranges_read_file(struct file *file, 345065b4c58SDimitris Papastamos char __user *user_buf, size_t count, 346065b4c58SDimitris Papastamos loff_t *ppos) 347065b4c58SDimitris Papastamos { 348065b4c58SDimitris Papastamos struct regmap *map = file->private_data; 349065b4c58SDimitris Papastamos struct regmap_debugfs_off_cache *c; 350065b4c58SDimitris Papastamos loff_t p = 0; 351065b4c58SDimitris Papastamos size_t buf_pos = 0; 352065b4c58SDimitris Papastamos char *buf; 353065b4c58SDimitris Papastamos char *entry; 354065b4c58SDimitris Papastamos int ret; 355e34dc490SRasmus Villemoes unsigned entry_len; 356065b4c58SDimitris Papastamos 357065b4c58SDimitris Papastamos if (*ppos < 0 || !count) 358065b4c58SDimitris Papastamos return -EINVAL; 359065b4c58SDimitris Papastamos 360065b4c58SDimitris Papastamos buf = kmalloc(count, GFP_KERNEL); 361065b4c58SDimitris Papastamos if (!buf) 362065b4c58SDimitris Papastamos return -ENOMEM; 363065b4c58SDimitris Papastamos 364065b4c58SDimitris Papastamos entry = kmalloc(PAGE_SIZE, GFP_KERNEL); 365065b4c58SDimitris Papastamos if (!entry) { 366065b4c58SDimitris Papastamos kfree(buf); 367065b4c58SDimitris Papastamos return -ENOMEM; 368065b4c58SDimitris Papastamos } 369065b4c58SDimitris Papastamos 370065b4c58SDimitris Papastamos /* While we are at it, build the register dump cache 371065b4c58SDimitris Papastamos * now so the read() operation on the `registers' file 372065b4c58SDimitris Papastamos * can benefit from using the cache. We do not care 373065b4c58SDimitris Papastamos * about the file position information that is contained 374065b4c58SDimitris Papastamos * in the cache, just about the actual register blocks */ 375065b4c58SDimitris Papastamos regmap_calc_tot_len(map, buf, count); 376065b4c58SDimitris Papastamos regmap_debugfs_get_dump_start(map, 0, *ppos, &p); 377065b4c58SDimitris Papastamos 378065b4c58SDimitris Papastamos /* Reset file pointer as the fixed-format of the `registers' 379065b4c58SDimitris Papastamos * file is not compatible with the `range' file */ 380065b4c58SDimitris Papastamos p = 0; 381065b4c58SDimitris Papastamos mutex_lock(&map->cache_lock); 382065b4c58SDimitris Papastamos list_for_each_entry(c, &map->debugfs_off_cache, list) { 383ca07e9f3SRasmus Villemoes entry_len = snprintf(entry, PAGE_SIZE, "%x-%x\n", 384065b4c58SDimitris Papastamos c->base_reg, c->max_reg); 385065b4c58SDimitris Papastamos if (p >= *ppos) { 386ca07e9f3SRasmus Villemoes if (buf_pos + entry_len > count) 387065b4c58SDimitris Papastamos break; 38820991cdbSRasmus Villemoes memcpy(buf + buf_pos, entry, entry_len); 389e34dc490SRasmus Villemoes buf_pos += entry_len; 390065b4c58SDimitris Papastamos } 391ca07e9f3SRasmus Villemoes p += entry_len; 392065b4c58SDimitris Papastamos } 393065b4c58SDimitris Papastamos mutex_unlock(&map->cache_lock); 394065b4c58SDimitris Papastamos 395065b4c58SDimitris Papastamos kfree(entry); 396065b4c58SDimitris Papastamos ret = buf_pos; 397065b4c58SDimitris Papastamos 398065b4c58SDimitris Papastamos if (copy_to_user(user_buf, buf, buf_pos)) { 399065b4c58SDimitris Papastamos ret = -EFAULT; 400065b4c58SDimitris Papastamos goto out_buf; 401065b4c58SDimitris Papastamos } 402065b4c58SDimitris Papastamos 403065b4c58SDimitris Papastamos *ppos += buf_pos; 404065b4c58SDimitris Papastamos out_buf: 405065b4c58SDimitris Papastamos kfree(buf); 406065b4c58SDimitris Papastamos return ret; 407065b4c58SDimitris Papastamos } 408065b4c58SDimitris Papastamos 409065b4c58SDimitris Papastamos static const struct file_operations regmap_reg_ranges_fops = { 410065b4c58SDimitris Papastamos .open = simple_open, 411065b4c58SDimitris Papastamos .read = regmap_reg_ranges_read_file, 412065b4c58SDimitris Papastamos .llseek = default_llseek, 413065b4c58SDimitris Papastamos }; 414065b4c58SDimitris Papastamos 4158da61f24SMark Brown static int regmap_access_show(struct seq_file *s, void *ignored) 416449e3842SMark Brown { 4178da61f24SMark Brown struct regmap *map = s->private; 4188da61f24SMark Brown int i, reg_len; 419449e3842SMark Brown 4209ae3109dSMark Brown reg_len = regmap_calc_reg_len(map->max_register); 421449e3842SMark Brown 422f01ee60fSStephen Warren for (i = 0; i <= map->max_register; i += map->reg_stride) { 423449e3842SMark Brown /* Ignore registers which are neither readable nor writable */ 424449e3842SMark Brown if (!regmap_readable(map, i) && !regmap_writeable(map, i)) 425449e3842SMark Brown continue; 426449e3842SMark Brown 427449e3842SMark Brown /* Format the register */ 4288da61f24SMark Brown seq_printf(s, "%.*x: %c %c %c %c\n", reg_len, i, 429449e3842SMark Brown regmap_readable(map, i) ? 'y' : 'n', 430449e3842SMark Brown regmap_writeable(map, i) ? 'y' : 'n', 431449e3842SMark Brown regmap_volatile(map, i) ? 'y' : 'n', 432449e3842SMark Brown regmap_precious(map, i) ? 'y' : 'n'); 433449e3842SMark Brown } 434449e3842SMark Brown 4358da61f24SMark Brown return 0; 436449e3842SMark Brown } 437449e3842SMark Brown 4388da61f24SMark Brown static int access_open(struct inode *inode, struct file *file) 4398da61f24SMark Brown { 4408da61f24SMark Brown return single_open(file, regmap_access_show, inode->i_private); 441449e3842SMark Brown } 442449e3842SMark Brown 443449e3842SMark Brown static const struct file_operations regmap_access_fops = { 4448da61f24SMark Brown .open = access_open, 4458da61f24SMark Brown .read = seq_read, 4468da61f24SMark Brown .llseek = seq_lseek, 4478da61f24SMark Brown .release = single_release, 448449e3842SMark Brown }; 44931244e39SMark Brown 450d3dc5430SRichard Fitzgerald static ssize_t regmap_cache_only_write_file(struct file *file, 451d3dc5430SRichard Fitzgerald const char __user *user_buf, 452d3dc5430SRichard Fitzgerald size_t count, loff_t *ppos) 453d3dc5430SRichard Fitzgerald { 454d3dc5430SRichard Fitzgerald struct regmap *map = container_of(file->private_data, 455d3dc5430SRichard Fitzgerald struct regmap, cache_only); 456d3dc5430SRichard Fitzgerald ssize_t result; 457d3dc5430SRichard Fitzgerald bool was_enabled, require_sync = false; 458d3dc5430SRichard Fitzgerald int err; 459d3dc5430SRichard Fitzgerald 460d3dc5430SRichard Fitzgerald map->lock(map->lock_arg); 461d3dc5430SRichard Fitzgerald 462d3dc5430SRichard Fitzgerald was_enabled = map->cache_only; 463d3dc5430SRichard Fitzgerald 464d3dc5430SRichard Fitzgerald result = debugfs_write_file_bool(file, user_buf, count, ppos); 465d3dc5430SRichard Fitzgerald if (result < 0) { 466d3dc5430SRichard Fitzgerald map->unlock(map->lock_arg); 467d3dc5430SRichard Fitzgerald return result; 468d3dc5430SRichard Fitzgerald } 469d3dc5430SRichard Fitzgerald 470d3dc5430SRichard Fitzgerald if (map->cache_only && !was_enabled) { 471d3dc5430SRichard Fitzgerald dev_warn(map->dev, "debugfs cache_only=Y forced\n"); 472d3dc5430SRichard Fitzgerald add_taint(TAINT_USER, LOCKDEP_STILL_OK); 473d3dc5430SRichard Fitzgerald } else if (!map->cache_only && was_enabled) { 474d3dc5430SRichard Fitzgerald dev_warn(map->dev, "debugfs cache_only=N forced: syncing cache\n"); 475d3dc5430SRichard Fitzgerald require_sync = true; 476d3dc5430SRichard Fitzgerald } 477d3dc5430SRichard Fitzgerald 478d3dc5430SRichard Fitzgerald map->unlock(map->lock_arg); 479d3dc5430SRichard Fitzgerald 480d3dc5430SRichard Fitzgerald if (require_sync) { 481d3dc5430SRichard Fitzgerald err = regcache_sync(map); 482d3dc5430SRichard Fitzgerald if (err) 483d3dc5430SRichard Fitzgerald dev_err(map->dev, "Failed to sync cache %d\n", err); 484d3dc5430SRichard Fitzgerald } 485d3dc5430SRichard Fitzgerald 486d3dc5430SRichard Fitzgerald return result; 487d3dc5430SRichard Fitzgerald } 488d3dc5430SRichard Fitzgerald 489d3dc5430SRichard Fitzgerald static const struct file_operations regmap_cache_only_fops = { 490d3dc5430SRichard Fitzgerald .open = simple_open, 491d3dc5430SRichard Fitzgerald .read = debugfs_read_file_bool, 492d3dc5430SRichard Fitzgerald .write = regmap_cache_only_write_file, 493d3dc5430SRichard Fitzgerald }; 494d3dc5430SRichard Fitzgerald 495d3dc5430SRichard Fitzgerald static ssize_t regmap_cache_bypass_write_file(struct file *file, 496d3dc5430SRichard Fitzgerald const char __user *user_buf, 497d3dc5430SRichard Fitzgerald size_t count, loff_t *ppos) 498d3dc5430SRichard Fitzgerald { 499d3dc5430SRichard Fitzgerald struct regmap *map = container_of(file->private_data, 500d3dc5430SRichard Fitzgerald struct regmap, cache_bypass); 501d3dc5430SRichard Fitzgerald ssize_t result; 502d3dc5430SRichard Fitzgerald bool was_enabled; 503d3dc5430SRichard Fitzgerald 504d3dc5430SRichard Fitzgerald map->lock(map->lock_arg); 505d3dc5430SRichard Fitzgerald 506d3dc5430SRichard Fitzgerald was_enabled = map->cache_bypass; 507d3dc5430SRichard Fitzgerald 508d3dc5430SRichard Fitzgerald result = debugfs_write_file_bool(file, user_buf, count, ppos); 509d3dc5430SRichard Fitzgerald if (result < 0) 510d3dc5430SRichard Fitzgerald goto out; 511d3dc5430SRichard Fitzgerald 512d3dc5430SRichard Fitzgerald if (map->cache_bypass && !was_enabled) { 513d3dc5430SRichard Fitzgerald dev_warn(map->dev, "debugfs cache_bypass=Y forced\n"); 514d3dc5430SRichard Fitzgerald add_taint(TAINT_USER, LOCKDEP_STILL_OK); 515d3dc5430SRichard Fitzgerald } else if (!map->cache_bypass && was_enabled) { 516d3dc5430SRichard Fitzgerald dev_warn(map->dev, "debugfs cache_bypass=N forced\n"); 517d3dc5430SRichard Fitzgerald } 518d3dc5430SRichard Fitzgerald 519d3dc5430SRichard Fitzgerald out: 520d3dc5430SRichard Fitzgerald map->unlock(map->lock_arg); 521d3dc5430SRichard Fitzgerald 522d3dc5430SRichard Fitzgerald return result; 523d3dc5430SRichard Fitzgerald } 524d3dc5430SRichard Fitzgerald 525d3dc5430SRichard Fitzgerald static const struct file_operations regmap_cache_bypass_fops = { 526d3dc5430SRichard Fitzgerald .open = simple_open, 527d3dc5430SRichard Fitzgerald .read = debugfs_read_file_bool, 528d3dc5430SRichard Fitzgerald .write = regmap_cache_bypass_write_file, 529d3dc5430SRichard Fitzgerald }; 530d3dc5430SRichard Fitzgerald 531d3c242e1SStephen Warren void regmap_debugfs_init(struct regmap *map, const char *name) 53231244e39SMark Brown { 5334b020b3fSMark Brown struct rb_node *next; 5344b020b3fSMark Brown struct regmap_range_node *range_node; 5352c98e0c1SXiubo Li const char *devname = "dummy"; 5364b020b3fSMark Brown 537078711d7SBartosz Golaszewski /* 538078711d7SBartosz Golaszewski * Userspace can initiate reads from the hardware over debugfs. 539078711d7SBartosz Golaszewski * Normally internal regmap structures and buffers are protected with 540078711d7SBartosz Golaszewski * a mutex or a spinlock, but if the regmap owner decided to disable 541078711d7SBartosz Golaszewski * all locking mechanisms, this is no longer the case. For safety: 542078711d7SBartosz Golaszewski * don't create the debugfs entries if locking is disabled. 543078711d7SBartosz Golaszewski */ 544a5ba91c3SBartosz Golaszewski if (map->debugfs_disable) { 545a5ba91c3SBartosz Golaszewski dev_dbg(map->dev, "regmap locking disabled - not creating debugfs entries\n"); 54672465736SMark Brown return; 547a5ba91c3SBartosz Golaszewski } 54872465736SMark Brown 549a52eaeb1STero Kristo /* If we don't have the debugfs root yet, postpone init */ 550a52eaeb1STero Kristo if (!regmap_debugfs_root) { 551a52eaeb1STero Kristo struct regmap_debugfs_node *node; 552a52eaeb1STero Kristo node = kzalloc(sizeof(*node), GFP_KERNEL); 553a52eaeb1STero Kristo if (!node) 554a52eaeb1STero Kristo return; 555a52eaeb1STero Kristo node->map = map; 556a52eaeb1STero Kristo node->name = name; 557a52eaeb1STero Kristo mutex_lock(®map_debugfs_early_lock); 558a52eaeb1STero Kristo list_add(&node->link, ®map_debugfs_early_list); 559a52eaeb1STero Kristo mutex_unlock(®map_debugfs_early_lock); 560a52eaeb1STero Kristo return; 561a52eaeb1STero Kristo } 562a52eaeb1STero Kristo 5635166b7c0SMark Brown INIT_LIST_HEAD(&map->debugfs_off_cache); 564065b4c58SDimitris Papastamos mutex_init(&map->cache_lock); 5655166b7c0SMark Brown 5662c98e0c1SXiubo Li if (map->dev) 5672c98e0c1SXiubo Li devname = dev_name(map->dev); 5682c98e0c1SXiubo Li 569d3c242e1SStephen Warren if (name) { 570d3c242e1SStephen Warren map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", 5712c98e0c1SXiubo Li devname, name); 572d3c242e1SStephen Warren name = map->debugfs_name; 573d3c242e1SStephen Warren } else { 5742c98e0c1SXiubo Li name = devname; 575d3c242e1SStephen Warren } 576d3c242e1SStephen Warren 577a430ab20SFabio Estevam if (!strcmp(name, "dummy")) { 57846589e9cSMark Brown map->debugfs_name = kasprintf(GFP_KERNEL, "dummy%d", 57946589e9cSMark Brown dummy_index); 58046589e9cSMark Brown name = map->debugfs_name; 581a430ab20SFabio Estevam dummy_index++; 582a430ab20SFabio Estevam } 583a430ab20SFabio Estevam 584d3c242e1SStephen Warren map->debugfs = debugfs_create_dir(name, regmap_debugfs_root); 58531244e39SMark Brown if (!map->debugfs) { 58631244e39SMark Brown dev_warn(map->dev, "Failed to create debugfs directory\n"); 587*17cf46cfSJeffy Chen 588*17cf46cfSJeffy Chen kfree(map->debugfs_name); 589*17cf46cfSJeffy Chen map->debugfs_name = NULL; 59031244e39SMark Brown return; 59131244e39SMark Brown } 59231244e39SMark Brown 593f0c2319fSDimitris Papastamos debugfs_create_file("name", 0400, map->debugfs, 594f0c2319fSDimitris Papastamos map, ®map_name_fops); 595f0c2319fSDimitris Papastamos 596065b4c58SDimitris Papastamos debugfs_create_file("range", 0400, map->debugfs, 597065b4c58SDimitris Papastamos map, ®map_reg_ranges_fops); 598065b4c58SDimitris Papastamos 599676970daSPawel Moll if (map->max_register || regmap_readable(map, 0)) { 600ffff7a12SMarkus Pargmann umode_t registers_mode; 601ffff7a12SMarkus Pargmann 6021635e888SAxel Lin #if defined(REGMAP_ALLOW_WRITE_DEBUGFS) 603ffff7a12SMarkus Pargmann registers_mode = 0600; 6041635e888SAxel Lin #else 605ffff7a12SMarkus Pargmann registers_mode = 0400; 6061635e888SAxel Lin #endif 607ffff7a12SMarkus Pargmann 608ffff7a12SMarkus Pargmann debugfs_create_file("registers", registers_mode, map->debugfs, 60931244e39SMark Brown map, ®map_map_fops); 610449e3842SMark Brown debugfs_create_file("access", 0400, map->debugfs, 611449e3842SMark Brown map, ®map_access_fops); 612449e3842SMark Brown } 613028a01e6SMark Brown 614028a01e6SMark Brown if (map->cache_type) { 615d3dc5430SRichard Fitzgerald debugfs_create_file("cache_only", 0600, map->debugfs, 616d3dc5430SRichard Fitzgerald &map->cache_only, ®map_cache_only_fops); 617028a01e6SMark Brown debugfs_create_bool("cache_dirty", 0400, map->debugfs, 618028a01e6SMark Brown &map->cache_dirty); 619d3dc5430SRichard Fitzgerald debugfs_create_file("cache_bypass", 0600, map->debugfs, 620d3dc5430SRichard Fitzgerald &map->cache_bypass, 621d3dc5430SRichard Fitzgerald ®map_cache_bypass_fops); 622028a01e6SMark Brown } 6234b020b3fSMark Brown 6244b020b3fSMark Brown next = rb_first(&map->range_tree); 6254b020b3fSMark Brown while (next) { 6264b020b3fSMark Brown range_node = rb_entry(next, struct regmap_range_node, node); 6274b020b3fSMark Brown 6284b020b3fSMark Brown if (range_node->name) 6294b020b3fSMark Brown debugfs_create_file(range_node->name, 0400, 6304b020b3fSMark Brown map->debugfs, range_node, 6314b020b3fSMark Brown ®map_range_fops); 6324b020b3fSMark Brown 6334b020b3fSMark Brown next = rb_next(&range_node->node); 6344b020b3fSMark Brown } 6355e0cbe78SLars-Peter Clausen 6365e0cbe78SLars-Peter Clausen if (map->cache_ops && map->cache_ops->debugfs_init) 6375e0cbe78SLars-Peter Clausen map->cache_ops->debugfs_init(map); 63831244e39SMark Brown } 63931244e39SMark Brown 64031244e39SMark Brown void regmap_debugfs_exit(struct regmap *map) 64131244e39SMark Brown { 642a52eaeb1STero Kristo if (map->debugfs) { 64331244e39SMark Brown debugfs_remove_recursive(map->debugfs); 644065b4c58SDimitris Papastamos mutex_lock(&map->cache_lock); 64595f971c7SMark Brown regmap_debugfs_free_dump_cache(map); 646065b4c58SDimitris Papastamos mutex_unlock(&map->cache_lock); 647d3c242e1SStephen Warren kfree(map->debugfs_name); 648a52eaeb1STero Kristo } else { 649a52eaeb1STero Kristo struct regmap_debugfs_node *node, *tmp; 650a52eaeb1STero Kristo 651a52eaeb1STero Kristo mutex_lock(®map_debugfs_early_lock); 652a52eaeb1STero Kristo list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list, 653a52eaeb1STero Kristo link) { 654a52eaeb1STero Kristo if (node->map == map) { 655a52eaeb1STero Kristo list_del(&node->link); 656a52eaeb1STero Kristo kfree(node); 657a52eaeb1STero Kristo } 658a52eaeb1STero Kristo } 659a52eaeb1STero Kristo mutex_unlock(®map_debugfs_early_lock); 660a52eaeb1STero Kristo } 66131244e39SMark Brown } 66231244e39SMark Brown 66331244e39SMark Brown void regmap_debugfs_initcall(void) 66431244e39SMark Brown { 665a52eaeb1STero Kristo struct regmap_debugfs_node *node, *tmp; 666a52eaeb1STero Kristo 66731244e39SMark Brown regmap_debugfs_root = debugfs_create_dir("regmap", NULL); 66831244e39SMark Brown if (!regmap_debugfs_root) { 66931244e39SMark Brown pr_warn("regmap: Failed to create debugfs root\n"); 67031244e39SMark Brown return; 67131244e39SMark Brown } 672a52eaeb1STero Kristo 673a52eaeb1STero Kristo mutex_lock(®map_debugfs_early_lock); 674a52eaeb1STero Kristo list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list, link) { 675a52eaeb1STero Kristo regmap_debugfs_init(node->map, node->name); 676a52eaeb1STero Kristo list_del(&node->link); 677a52eaeb1STero Kristo kfree(node); 678a52eaeb1STero Kristo } 679a52eaeb1STero Kristo mutex_unlock(®map_debugfs_early_lock); 68031244e39SMark Brown } 681