diff options
Diffstat (limited to 'drivers/base/regmap')
-rw-r--r-- | drivers/base/regmap/internal.h | 14 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-debugfs.c | 102 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-irq.c | 19 |
3 files changed, 122 insertions, 13 deletions
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 288e135fd5f..401d1919635 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -15,10 +15,18 @@ #include <linux/regmap.h> #include <linux/fs.h> +#include <linux/list.h> struct regmap; struct regcache_ops; +struct regmap_debugfs_off_cache { + struct list_head list; + off_t min; + off_t max; + unsigned int base_reg; +}; + struct regmap_format { size_t buf_size; size_t reg_bytes; @@ -48,6 +56,12 @@ struct regmap { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; const char *debugfs_name; + + unsigned int debugfs_reg_len; + unsigned int debugfs_val_len; + unsigned int debugfs_tot_len; + + struct list_head debugfs_off_cache; #endif unsigned int max_register; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index f4b9dd01c98..07aad786f81 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -56,17 +56,74 @@ static const struct file_operations regmap_name_fops = { .llseek = default_llseek, }; +/* + * Work out where the start offset maps into register numbers, bearing + * in mind that we suppress hidden registers. + */ +static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, + unsigned int base, + loff_t from, + loff_t *pos) +{ + struct regmap_debugfs_off_cache *c = NULL; + loff_t p = 0; + unsigned int i, ret; + + /* + * If we don't have a cache build one so we don't have to do a + * linear scan each time. + */ + if (list_empty(&map->debugfs_off_cache)) { + for (i = base; i <= map->max_register; i += map->reg_stride) { + /* Skip unprinted registers, closing off cache entry */ + if (!regmap_readable(map, i) || + regmap_precious(map, i)) { + if (c) { + c->max = p - 1; + list_add_tail(&c->list, + &map->debugfs_off_cache); + c = NULL; + } + + continue; + } + + /* No cache entry? Start a new one */ + if (!c) { + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + break; + c->min = p; + c->base_reg = i; + } + + p += map->debugfs_tot_len; + } + } + + /* Find the relevant block */ + list_for_each_entry(c, &map->debugfs_off_cache, list) { + if (*pos >= c->min && *pos <= c->max) { + *pos = c->min; + return c->base_reg; + } + + ret = c->max; + } + + return ret; +} + static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, unsigned int to, char __user *user_buf, size_t count, loff_t *ppos) { - int reg_len, val_len, tot_len; size_t buf_pos = 0; - loff_t p = 0; + loff_t p = *ppos; ssize_t ret; int i; char *buf; - unsigned int val; + unsigned int val, start_reg; if (*ppos < 0 || !count) return -EINVAL; @@ -76,11 +133,18 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, return -ENOMEM; /* Calculate the length of a fixed format */ - reg_len = regmap_calc_reg_len(map->max_register, buf, count); - val_len = 2 * map->format.val_bytes; - tot_len = reg_len + val_len + 3; /* : \n */ + if (!map->debugfs_tot_len) { + map->debugfs_reg_len = regmap_calc_reg_len(map->max_register, + buf, count); + map->debugfs_val_len = 2 * map->format.val_bytes; + map->debugfs_tot_len = map->debugfs_reg_len + + map->debugfs_val_len + 3; /* : \n */ + } + + /* Work out which register we're starting at */ + start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p); - for (i = from; i <= to; i += map->reg_stride) { + for (i = start_reg; i <= to; i += map->reg_stride) { if (!regmap_readable(map, i)) continue; @@ -90,26 +154,27 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, /* If we're in the region the user is trying to read */ if (p >= *ppos) { /* ...but not beyond it */ - if (buf_pos >= count - 1 - tot_len) + if (buf_pos + 1 + map->debugfs_tot_len >= count) break; /* Format the register */ snprintf(buf + buf_pos, count - buf_pos, "%.*x: ", - reg_len, i - from); - buf_pos += reg_len + 2; + map->debugfs_reg_len, i - from); + buf_pos += map->debugfs_reg_len + 2; /* Format the value, write all X if we can't read */ ret = regmap_read(map, i, &val); if (ret == 0) snprintf(buf + buf_pos, count - buf_pos, - "%.*x", val_len, val); + "%.*x", map->debugfs_val_len, val); else - memset(buf + buf_pos, 'X', val_len); + memset(buf + buf_pos, 'X', + map->debugfs_val_len); buf_pos += 2 * map->format.val_bytes; buf[buf_pos++] = '\n'; } - p += tot_len; + p += map->debugfs_tot_len; } ret = buf_pos; @@ -272,6 +337,8 @@ void regmap_debugfs_init(struct regmap *map, const char *name) struct rb_node *next; struct regmap_range_node *range_node; + INIT_LIST_HEAD(&map->debugfs_off_cache); + if (name) { map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", dev_name(map->dev), name); @@ -320,7 +387,16 @@ void regmap_debugfs_init(struct regmap *map, const char *name) void regmap_debugfs_exit(struct regmap *map) { + struct regmap_debugfs_off_cache *c; + debugfs_remove_recursive(map->debugfs); + while (!list_empty(&map->debugfs_off_cache)) { + c = list_first_entry(&map->debugfs_off_cache, + struct regmap_debugfs_off_cache, + list); + list_del(&c->list); + kfree(c); + } kfree(map->debugfs_name); } diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 5b6b1d8e6cc..5972ad95854 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -458,3 +458,22 @@ int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq) return irq_create_mapping(data->domain, irq); } EXPORT_SYMBOL_GPL(regmap_irq_get_virq); + +/** + * regmap_irq_get_domain(): Retrieve the irq_domain for the chip + * + * Useful for drivers to request their own IRQs and for integration + * with subsystems. For ease of integration NULL is accepted as a + * domain, allowing devices to just call this even if no domain is + * allocated. + * + * @data: regmap_irq controller to operate on. + */ +struct irq_domain *regmap_irq_get_domain(struct regmap_irq_chip_data *data) +{ + if (data) + return data->domain; + else + return NULL; +} +EXPORT_SYMBOL_GPL(regmap_irq_get_domain); |