--- zzzz-none-000/linux-3.10.107/drivers/base/regmap/regmap-debugfs.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/base/regmap/regmap-debugfs.c 2021-02-04 17:41:59.000000000 +0000 @@ -15,13 +15,22 @@ #include #include #include +#include #include "internal.h" +struct regmap_debugfs_node { + struct regmap *map; + const char *name; + struct list_head link; +}; + static struct dentry *regmap_debugfs_root; +static LIST_HEAD(regmap_debugfs_early_list); +static DEFINE_MUTEX(regmap_debugfs_early_lock); /* Calculate the length of a fixed format */ -static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size) +static size_t regmap_calc_reg_len(int max_val) { return snprintf(NULL, 0, "%x", max_val); } @@ -83,6 +92,10 @@ unsigned int fpos_offset; unsigned int reg_offset; + /* Suppress the cache if we're using a subrange */ + if (base) + return base; + /* * If we don't have a cache build one so we don't have to do a * linear scan each time. @@ -144,7 +157,7 @@ reg_offset = fpos_offset / map->debugfs_tot_len; *pos = c->min + (reg_offset * map->debugfs_tot_len); mutex_unlock(&map->cache_lock); - return c->base_reg + reg_offset; + return c->base_reg + (reg_offset * map->reg_stride); } *pos = c->max; @@ -160,8 +173,7 @@ { /* Calculate the length of a fixed format */ if (!map->debugfs_tot_len) { - map->debugfs_reg_len = regmap_calc_reg_len(map->max_register, - buf, count); + map->debugfs_reg_len = regmap_calc_reg_len(map->max_register), map->debugfs_val_len = 2 * map->format.val_bytes; map->debugfs_tot_len = map->debugfs_reg_len + map->debugfs_val_len + 3; /* : \n */ @@ -247,8 +259,7 @@ count, ppos); } -#undef REGMAP_ALLOW_WRITE_DEBUGFS -#ifdef REGMAP_ALLOW_WRITE_DEBUGFS +#ifdef CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS /* * This can be dangerous especially when we have clients such as * PMICs, therefore don't provide any real compile time configuration option @@ -276,11 +287,11 @@ reg = simple_strtoul(start, &start, 16); while (*start == ' ') start++; - if (strict_strtoul(start, 16, &value)) + if (kstrtoul(start, 16, &value)) return -EINVAL; /* Userspace has been fiddling around behind the kernel's back */ - add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); + add_taint(TAINT_USER, LOCKDEP_STILL_OK); ret = regmap_write(map, reg, value); if (ret < 0) @@ -298,6 +309,67 @@ .llseek = default_llseek, }; +static ssize_t regmap_data_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct regmap *map = file->private_data; + int new_count; + + regmap_calc_tot_len(map, NULL, 0); + new_count = map->dump_count * map->debugfs_tot_len; + if (new_count > count) + new_count = count; + + if (*ppos == 0) + *ppos = map->dump_address * map->debugfs_tot_len; + else if (*ppos >= map->dump_address * map->debugfs_tot_len + + map->dump_count * map->debugfs_tot_len) + return 0; + return regmap_read_debugfs(map, 0, map->max_register, user_buf, + new_count, ppos); +} + +#ifdef CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS +static ssize_t regmap_data_write_file(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[32]; + size_t buf_size; + char *start = buf; + unsigned long value; + struct regmap *map = file->private_data; + int ret; + + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + while (*start == ' ') + start++; + if (kstrtoul(start, 16, &value)) + return -EINVAL; + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + + ret = regmap_write(map, map->dump_address, value); + if (ret < 0) + return ret; + return buf_size; +} +#else +#define regmap_data_write_file NULL +#endif + +static const struct file_operations regmap_data_fops = { + .open = simple_open, + .read = regmap_data_read_file, + .write = regmap_data_write_file, + .llseek = default_llseek, +}; + static ssize_t regmap_range_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -325,6 +397,7 @@ char *buf; char *entry; int ret; + unsigned entry_len; if (*ppos < 0 || !count) return -EINVAL; @@ -352,18 +425,15 @@ p = 0; mutex_lock(&map->cache_lock); list_for_each_entry(c, &map->debugfs_off_cache, list) { - snprintf(entry, PAGE_SIZE, "%x-%x", - c->base_reg, c->max_reg); + entry_len = snprintf(entry, PAGE_SIZE, "%x-%x\n", + c->base_reg, c->max_reg); if (p >= *ppos) { - if (buf_pos + 1 + strlen(entry) > count) + if (buf_pos + entry_len > count) break; - snprintf(buf + buf_pos, count - buf_pos, - "%s", entry); - buf_pos += strlen(entry); - buf[buf_pos] = '\n'; - buf_pos++; + memcpy(buf + buf_pos, entry, entry_len); + buf_pos += entry_len; } - p += strlen(entry) + 1; + p += entry_len; } mutex_unlock(&map->cache_lock); @@ -407,7 +477,7 @@ return -ENOMEM; /* Calculate the length of a fixed format */ - reg_len = regmap_calc_reg_len(map->max_register, buf, count); + reg_len = regmap_calc_reg_len(map->max_register); tot_len = reg_len + 10; /* ': R W V P\n' */ for (i = 0; i <= map->max_register; i += map->reg_stride) { @@ -455,12 +525,107 @@ .llseek = default_llseek, }; +static ssize_t regmap_cache_only_write_file(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct regmap *map = container_of(file->private_data, + struct regmap, cache_only); + ssize_t result; + bool was_enabled, require_sync = false; + int err; + + map->lock(map->lock_arg); + + was_enabled = map->cache_only; + + result = debugfs_write_file_bool(file, user_buf, count, ppos); + if (result < 0) { + map->unlock(map->lock_arg); + return result; + } + + if (map->cache_only && !was_enabled) { + dev_warn(map->dev, "debugfs cache_only=Y forced\n"); + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + } else if (!map->cache_only && was_enabled) { + dev_warn(map->dev, "debugfs cache_only=N forced: syncing cache\n"); + require_sync = true; + } + + map->unlock(map->lock_arg); + + if (require_sync) { + err = regcache_sync(map); + if (err) + dev_err(map->dev, "Failed to sync cache %d\n", err); + } + + return result; +} + +static const struct file_operations regmap_cache_only_fops = { + .open = simple_open, + .read = debugfs_read_file_bool, + .write = regmap_cache_only_write_file, +}; + +static ssize_t regmap_cache_bypass_write_file(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct regmap *map = container_of(file->private_data, + struct regmap, cache_bypass); + ssize_t result; + bool was_enabled; + + map->lock(map->lock_arg); + + was_enabled = map->cache_bypass; + + result = debugfs_write_file_bool(file, user_buf, count, ppos); + if (result < 0) + goto out; + + if (map->cache_bypass && !was_enabled) { + dev_warn(map->dev, "debugfs cache_bypass=Y forced\n"); + add_taint(TAINT_USER, LOCKDEP_STILL_OK); + } else if (!map->cache_bypass && was_enabled) { + dev_warn(map->dev, "debugfs cache_bypass=N forced\n"); + } + +out: + map->unlock(map->lock_arg); + + return result; +} + +static const struct file_operations regmap_cache_bypass_fops = { + .open = simple_open, + .read = debugfs_read_file_bool, + .write = regmap_cache_bypass_write_file, +}; + void regmap_debugfs_init(struct regmap *map, const char *name) { struct rb_node *next; struct regmap_range_node *range_node; const char *devname = "dummy"; + /* If we don't have the debugfs root yet, postpone init */ + if (!regmap_debugfs_root) { + struct regmap_debugfs_node *node; + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return; + node->map = map; + node->name = name; + mutex_lock(®map_debugfs_early_lock); + list_add(&node->link, ®map_debugfs_early_list); + mutex_unlock(®map_debugfs_early_lock); + return; + } + INIT_LIST_HEAD(&map->debugfs_off_cache); mutex_init(&map->cache_lock); @@ -487,20 +652,38 @@ debugfs_create_file("range", 0400, map->debugfs, map, ®map_reg_ranges_fops); - if (map->max_register) { - debugfs_create_file("registers", 0400, map->debugfs, + if (map->max_register || regmap_readable(map, 0)) { + umode_t registers_mode; + +#ifdef CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS + registers_mode = 0600; +#else + registers_mode = 0400; +#endif + + debugfs_create_file("registers", registers_mode, map->debugfs, map, ®map_map_fops); + + debugfs_create_x32("address", 0600, map->debugfs, + &map->dump_address); + map->dump_count = 1; + debugfs_create_u32("count", 0600, map->debugfs, + &map->dump_count); + debugfs_create_file("data", registers_mode, map->debugfs, + map, ®map_data_fops); + debugfs_create_file("access", 0400, map->debugfs, map, ®map_access_fops); } if (map->cache_type) { - debugfs_create_bool("cache_only", 0400, map->debugfs, - &map->cache_only); + debugfs_create_file("cache_only", 0600, map->debugfs, + &map->cache_only, ®map_cache_only_fops); debugfs_create_bool("cache_dirty", 0400, map->debugfs, &map->cache_dirty); - debugfs_create_bool("cache_bypass", 0400, map->debugfs, - &map->cache_bypass); + debugfs_create_file("cache_bypass", 0600, map->debugfs, + &map->cache_bypass, + ®map_cache_bypass_fops); } next = rb_first(&map->range_tree); @@ -514,22 +697,49 @@ next = rb_next(&range_node->node); } + + if (map->cache_ops && map->cache_ops->debugfs_init) + map->cache_ops->debugfs_init(map); } void regmap_debugfs_exit(struct regmap *map) { - debugfs_remove_recursive(map->debugfs); - mutex_lock(&map->cache_lock); - regmap_debugfs_free_dump_cache(map); - mutex_unlock(&map->cache_lock); - kfree(map->debugfs_name); + if (map->debugfs) { + debugfs_remove_recursive(map->debugfs); + mutex_lock(&map->cache_lock); + regmap_debugfs_free_dump_cache(map); + mutex_unlock(&map->cache_lock); + kfree(map->debugfs_name); + } else { + struct regmap_debugfs_node *node, *tmp; + + mutex_lock(®map_debugfs_early_lock); + list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list, + link) { + if (node->map == map) { + list_del(&node->link); + kfree(node); + } + } + mutex_unlock(®map_debugfs_early_lock); + } } void regmap_debugfs_initcall(void) { + struct regmap_debugfs_node *node, *tmp; + regmap_debugfs_root = debugfs_create_dir("regmap", NULL); if (!regmap_debugfs_root) { pr_warn("regmap: Failed to create debugfs root\n"); return; } + + mutex_lock(®map_debugfs_early_lock); + list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list, link) { + regmap_debugfs_init(node->map, node->name); + list_del(&node->link); + kfree(node); + } + mutex_unlock(®map_debugfs_early_lock); }