137613fa5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
237613fa5SGreg Kroah-Hartman //
337613fa5SGreg Kroah-Hartman // Register map access API - debugfs
437613fa5SGreg Kroah-Hartman //
537613fa5SGreg Kroah-Hartman // Copyright 2011 Wolfson Microelectronics plc
637613fa5SGreg Kroah-Hartman //
737613fa5SGreg Kroah-Hartman // Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
831244e39SMark Brown
931244e39SMark Brown #include <linux/slab.h>
1031244e39SMark Brown #include <linux/mutex.h>
1131244e39SMark Brown #include <linux/debugfs.h>
1231244e39SMark Brown #include <linux/uaccess.h>
1351990e82SPaul Gortmaker #include <linux/device.h>
14a52eaeb1STero Kristo #include <linux/list.h>
1531244e39SMark Brown
1631244e39SMark Brown #include "internal.h"
1731244e39SMark Brown
18a52eaeb1STero Kristo struct regmap_debugfs_node {
19a52eaeb1STero Kristo struct regmap *map;
20a52eaeb1STero Kristo struct list_head link;
21a52eaeb1STero Kristo };
22a52eaeb1STero Kristo
23a430ab20SFabio Estevam static unsigned int dummy_index;
2431244e39SMark Brown static struct dentry *regmap_debugfs_root;
25a52eaeb1STero Kristo static LIST_HEAD(regmap_debugfs_early_list);
26a52eaeb1STero Kristo static DEFINE_MUTEX(regmap_debugfs_early_lock);
2731244e39SMark Brown
2821f55544SMark Brown /* Calculate the length of a fixed format */
regmap_calc_reg_len(int max_val)299ae3109dSMark Brown static size_t regmap_calc_reg_len(int max_val)
3021f55544SMark Brown {
31176fc2d5SMark Brown return snprintf(NULL, 0, "%x", max_val);
3221f55544SMark Brown }
3321f55544SMark Brown
regmap_name_read_file(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)34f0c2319fSDimitris Papastamos static ssize_t regmap_name_read_file(struct file *file,
35f0c2319fSDimitris Papastamos char __user *user_buf, size_t count,
36f0c2319fSDimitris Papastamos loff_t *ppos)
37f0c2319fSDimitris Papastamos {
38f0c2319fSDimitris Papastamos struct regmap *map = file->private_data;
3912ae3808SDavid Lechner const char *name = "nodev";
40f0c2319fSDimitris Papastamos int ret;
41f0c2319fSDimitris Papastamos char *buf;
42f0c2319fSDimitris Papastamos
43f0c2319fSDimitris Papastamos buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
44f0c2319fSDimitris Papastamos if (!buf)
45f0c2319fSDimitris Papastamos return -ENOMEM;
46f0c2319fSDimitris Papastamos
4712ae3808SDavid Lechner if (map->dev && map->dev->driver)
4812ae3808SDavid Lechner name = map->dev->driver->name;
4912ae3808SDavid Lechner
5012ae3808SDavid Lechner ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
51*957ca2d9SChristophe JAILLET if (ret >= PAGE_SIZE) {
52f0c2319fSDimitris Papastamos kfree(buf);
53f0c2319fSDimitris Papastamos return ret;
54f0c2319fSDimitris Papastamos }
55f0c2319fSDimitris Papastamos
56f0c2319fSDimitris Papastamos ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
57f0c2319fSDimitris Papastamos kfree(buf);
58f0c2319fSDimitris Papastamos return ret;
59f0c2319fSDimitris Papastamos }
60f0c2319fSDimitris Papastamos
61f0c2319fSDimitris Papastamos static const struct file_operations regmap_name_fops = {
62234e3405SStephen Boyd .open = simple_open,
63f0c2319fSDimitris Papastamos .read = regmap_name_read_file,
64f0c2319fSDimitris Papastamos .llseek = default_llseek,
65f0c2319fSDimitris Papastamos };
66f0c2319fSDimitris Papastamos
regmap_debugfs_free_dump_cache(struct regmap * map)6795f971c7SMark Brown static void regmap_debugfs_free_dump_cache(struct regmap *map)
6895f971c7SMark Brown {
6995f971c7SMark Brown struct regmap_debugfs_off_cache *c;
7095f971c7SMark Brown
7195f971c7SMark Brown while (!list_empty(&map->debugfs_off_cache)) {
7295f971c7SMark Brown c = list_first_entry(&map->debugfs_off_cache,
7395f971c7SMark Brown struct regmap_debugfs_off_cache,
7495f971c7SMark Brown list);
7595f971c7SMark Brown list_del(&c->list);
7695f971c7SMark Brown kfree(c);
7795f971c7SMark Brown }
7895f971c7SMark Brown }
7995f971c7SMark Brown
regmap_printable(struct regmap * map,unsigned int reg)80359a2f17SCristian Birsan static bool regmap_printable(struct regmap *map, unsigned int reg)
81359a2f17SCristian Birsan {
82359a2f17SCristian Birsan if (regmap_precious(map, reg))
83359a2f17SCristian Birsan return false;
84359a2f17SCristian Birsan
85359a2f17SCristian Birsan if (!regmap_readable(map, reg) && !regmap_cached(map, reg))
86359a2f17SCristian Birsan return false;
87359a2f17SCristian Birsan
88359a2f17SCristian Birsan return true;
89359a2f17SCristian Birsan }
90359a2f17SCristian Birsan
91afab2f7bSMark Brown /*
92afab2f7bSMark Brown * Work out where the start offset maps into register numbers, bearing
93afab2f7bSMark Brown * in mind that we suppress hidden registers.
94afab2f7bSMark Brown */
regmap_debugfs_get_dump_start(struct regmap * map,unsigned int base,loff_t from,loff_t * pos)95afab2f7bSMark Brown static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
96afab2f7bSMark Brown unsigned int base,
97afab2f7bSMark Brown loff_t from,
98afab2f7bSMark Brown loff_t *pos)
99afab2f7bSMark Brown {
1005166b7c0SMark Brown struct regmap_debugfs_off_cache *c = NULL;
1015166b7c0SMark Brown loff_t p = 0;
1025166b7c0SMark Brown unsigned int i, ret;
103c2c1ee66SDimitris Papastamos unsigned int fpos_offset;
104c2c1ee66SDimitris Papastamos unsigned int reg_offset;
105afab2f7bSMark Brown
106d6814a7dSMark Brown /* Suppress the cache if we're using a subrange */
10726ee4741SLars-Peter Clausen if (base)
10826ee4741SLars-Peter Clausen return base;
109d6814a7dSMark Brown
1105166b7c0SMark Brown /*
1115166b7c0SMark Brown * If we don't have a cache build one so we don't have to do a
1125166b7c0SMark Brown * linear scan each time.
1135166b7c0SMark Brown */
114065b4c58SDimitris Papastamos mutex_lock(&map->cache_lock);
115480738deSDimitris Papastamos i = base;
1165166b7c0SMark Brown if (list_empty(&map->debugfs_off_cache)) {
117480738deSDimitris Papastamos for (; i <= map->max_register; i += map->reg_stride) {
1185166b7c0SMark Brown /* Skip unprinted registers, closing off cache entry */
119359a2f17SCristian Birsan if (!regmap_printable(map, i)) {
1205166b7c0SMark Brown if (c) {
1215166b7c0SMark Brown c->max = p - 1;
122480738deSDimitris Papastamos c->max_reg = i - map->reg_stride;
1235166b7c0SMark Brown list_add_tail(&c->list,
1245166b7c0SMark Brown &map->debugfs_off_cache);
1255166b7c0SMark Brown c = NULL;
1265166b7c0SMark Brown }
127afab2f7bSMark Brown
128afab2f7bSMark Brown continue;
1295166b7c0SMark Brown }
130afab2f7bSMark Brown
1315166b7c0SMark Brown /* No cache entry? Start a new one */
1325166b7c0SMark Brown if (!c) {
1335166b7c0SMark Brown c = kzalloc(sizeof(*c), GFP_KERNEL);
13495f971c7SMark Brown if (!c) {
13595f971c7SMark Brown regmap_debugfs_free_dump_cache(map);
136065b4c58SDimitris Papastamos mutex_unlock(&map->cache_lock);
13795f971c7SMark Brown return base;
13895f971c7SMark Brown }
1395166b7c0SMark Brown c->min = p;
1405166b7c0SMark Brown c->base_reg = i;
141afab2f7bSMark Brown }
142afab2f7bSMark Brown
143afab2f7bSMark Brown p += map->debugfs_tot_len;
144afab2f7bSMark Brown }
1455166b7c0SMark Brown }
146afab2f7bSMark Brown
147e8d6539cSMark Brown /* Close the last entry off if we didn't scan beyond it */
148e8d6539cSMark Brown if (c) {
149e8d6539cSMark Brown c->max = p - 1;
150480738deSDimitris Papastamos c->max_reg = i - map->reg_stride;
151e8d6539cSMark Brown list_add_tail(&c->list,
152e8d6539cSMark Brown &map->debugfs_off_cache);
153e8d6539cSMark Brown }
154e8d6539cSMark Brown
1555bd9f4bbSMark Brown /*
1565bd9f4bbSMark Brown * This should never happen; we return above if we fail to
1575bd9f4bbSMark Brown * allocate and we should never be in this code if there are
1585bd9f4bbSMark Brown * no registers at all.
1595bd9f4bbSMark Brown */
1605bd9f4bbSMark Brown WARN_ON(list_empty(&map->debugfs_off_cache));
161a3471469SRussell King ret = base;
1625bd9f4bbSMark Brown
163cf57d607SDimitris Papastamos /* Find the relevant block:offset */
1645166b7c0SMark Brown list_for_each_entry(c, &map->debugfs_off_cache, list) {
1655a1d6d17SMark Brown if (from >= c->min && from <= c->max) {
166cf57d607SDimitris Papastamos fpos_offset = from - c->min;
167cf57d607SDimitris Papastamos reg_offset = fpos_offset / map->debugfs_tot_len;
168cf57d607SDimitris Papastamos *pos = c->min + (reg_offset * map->debugfs_tot_len);
169065b4c58SDimitris Papastamos mutex_unlock(&map->cache_lock);
170213fa5d9SSrinivas Kandagatla return c->base_reg + (reg_offset * map->reg_stride);
1715166b7c0SMark Brown }
1725166b7c0SMark Brown
173cf57d607SDimitris Papastamos *pos = c->max;
174cf57d607SDimitris Papastamos ret = c->max_reg;
1755166b7c0SMark Brown }
176065b4c58SDimitris Papastamos mutex_unlock(&map->cache_lock);
1775166b7c0SMark Brown
1785166b7c0SMark Brown return ret;
179afab2f7bSMark Brown }
180afab2f7bSMark Brown
regmap_calc_tot_len(struct regmap * map,void * buf,size_t count)1814dd7c553SDimitris Papastamos static inline void regmap_calc_tot_len(struct regmap *map,
1824dd7c553SDimitris Papastamos void *buf, size_t count)
1834dd7c553SDimitris Papastamos {
1844dd7c553SDimitris Papastamos /* Calculate the length of a fixed format */
1854dd7c553SDimitris Papastamos if (!map->debugfs_tot_len) {
1867f4a122dSJulia Lawall map->debugfs_reg_len = regmap_calc_reg_len(map->max_register);
1874dd7c553SDimitris Papastamos map->debugfs_val_len = 2 * map->format.val_bytes;
1884dd7c553SDimitris Papastamos map->debugfs_tot_len = map->debugfs_reg_len +
1894dd7c553SDimitris Papastamos map->debugfs_val_len + 3; /* : \n */
1904dd7c553SDimitris Papastamos }
1914dd7c553SDimitris Papastamos }
1924dd7c553SDimitris Papastamos
regmap_next_readable_reg(struct regmap * map,int reg)193cc6a8d69SLucas Tanure static int regmap_next_readable_reg(struct regmap *map, int reg)
194cc6a8d69SLucas Tanure {
195cc6a8d69SLucas Tanure struct regmap_debugfs_off_cache *c;
196cc6a8d69SLucas Tanure int ret = -EINVAL;
197cc6a8d69SLucas Tanure
198cc6a8d69SLucas Tanure if (regmap_printable(map, reg + map->reg_stride)) {
199cc6a8d69SLucas Tanure ret = reg + map->reg_stride;
200cc6a8d69SLucas Tanure } else {
201cc6a8d69SLucas Tanure mutex_lock(&map->cache_lock);
202cc6a8d69SLucas Tanure list_for_each_entry(c, &map->debugfs_off_cache, list) {
203cc6a8d69SLucas Tanure if (reg > c->max_reg)
204cc6a8d69SLucas Tanure continue;
205cc6a8d69SLucas Tanure if (reg < c->base_reg) {
206cc6a8d69SLucas Tanure ret = c->base_reg;
207cc6a8d69SLucas Tanure break;
208cc6a8d69SLucas Tanure }
209cc6a8d69SLucas Tanure }
210cc6a8d69SLucas Tanure mutex_unlock(&map->cache_lock);
211cc6a8d69SLucas Tanure }
212cc6a8d69SLucas Tanure return ret;
213cc6a8d69SLucas Tanure }
214cc6a8d69SLucas Tanure
regmap_read_debugfs(struct regmap * map,unsigned int from,unsigned int to,char __user * user_buf,size_t count,loff_t * ppos)215bd9cc12fSMark Brown static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
216bd9cc12fSMark Brown unsigned int to, char __user *user_buf,
21731244e39SMark Brown size_t count, loff_t *ppos)
21831244e39SMark Brown {
21931244e39SMark Brown size_t buf_pos = 0;
220afab2f7bSMark Brown loff_t p = *ppos;
22131244e39SMark Brown ssize_t ret;
22231244e39SMark Brown int i;
22331244e39SMark Brown char *buf;
224afab2f7bSMark Brown unsigned int val, start_reg;
22531244e39SMark Brown
22631244e39SMark Brown if (*ppos < 0 || !count)
22731244e39SMark Brown return -EINVAL;
22831244e39SMark Brown
22923baf831SKirill A. Shutemov if (count > (PAGE_SIZE << MAX_ORDER))
23023baf831SKirill A. Shutemov count = PAGE_SIZE << MAX_ORDER;
23174edd08aSPeng Fan
23231244e39SMark Brown buf = kmalloc(count, GFP_KERNEL);
23331244e39SMark Brown if (!buf)
23431244e39SMark Brown return -ENOMEM;
23531244e39SMark Brown
2364dd7c553SDimitris Papastamos regmap_calc_tot_len(map, buf, count);
23731244e39SMark Brown
238afab2f7bSMark Brown /* Work out which register we're starting at */
239afab2f7bSMark Brown start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p);
240afab2f7bSMark Brown
241cc6a8d69SLucas Tanure for (i = start_reg; i >= 0 && i <= to;
242cc6a8d69SLucas Tanure i = regmap_next_readable_reg(map, i)) {
2432efe1642SMark Brown
24431244e39SMark Brown /* If we're in the region the user is trying to read */
24531244e39SMark Brown if (p >= *ppos) {
24631244e39SMark Brown /* ...but not beyond it */
247f3eb8399SDimitris Papastamos if (buf_pos + map->debugfs_tot_len > count)
24831244e39SMark Brown break;
24931244e39SMark Brown
25031244e39SMark Brown /* Format the register */
25131244e39SMark Brown snprintf(buf + buf_pos, count - buf_pos, "%.*x: ",
252cbc1938bSMark Brown map->debugfs_reg_len, i - from);
253cbc1938bSMark Brown buf_pos += map->debugfs_reg_len + 2;
25431244e39SMark Brown
25531244e39SMark Brown /* Format the value, write all X if we can't read */
25631244e39SMark Brown ret = regmap_read(map, i, &val);
25731244e39SMark Brown if (ret == 0)
25831244e39SMark Brown snprintf(buf + buf_pos, count - buf_pos,
259cbc1938bSMark Brown "%.*x", map->debugfs_val_len, val);
26031244e39SMark Brown else
261cbc1938bSMark Brown memset(buf + buf_pos, 'X',
262cbc1938bSMark Brown map->debugfs_val_len);
26331244e39SMark Brown buf_pos += 2 * map->format.val_bytes;
26431244e39SMark Brown
26531244e39SMark Brown buf[buf_pos++] = '\n';
26631244e39SMark Brown }
267cbc1938bSMark Brown p += map->debugfs_tot_len;
26831244e39SMark Brown }
26931244e39SMark Brown
27031244e39SMark Brown ret = buf_pos;
27131244e39SMark Brown
27231244e39SMark Brown if (copy_to_user(user_buf, buf, buf_pos)) {
27331244e39SMark Brown ret = -EFAULT;
27431244e39SMark Brown goto out;
27531244e39SMark Brown }
27631244e39SMark Brown
27731244e39SMark Brown *ppos += buf_pos;
27831244e39SMark Brown
27931244e39SMark Brown out:
28031244e39SMark Brown kfree(buf);
28131244e39SMark Brown return ret;
28231244e39SMark Brown }
28331244e39SMark Brown
regmap_map_read_file(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)284bd9cc12fSMark Brown static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
285bd9cc12fSMark Brown size_t count, loff_t *ppos)
286bd9cc12fSMark Brown {
287bd9cc12fSMark Brown struct regmap *map = file->private_data;
288bd9cc12fSMark Brown
289bd9cc12fSMark Brown return regmap_read_debugfs(map, 0, map->max_register, user_buf,
290bd9cc12fSMark Brown count, ppos);
291bd9cc12fSMark Brown }
292bd9cc12fSMark Brown
29309c6ecd3SDimitris Papastamos #undef REGMAP_ALLOW_WRITE_DEBUGFS
29409c6ecd3SDimitris Papastamos #ifdef REGMAP_ALLOW_WRITE_DEBUGFS
29509c6ecd3SDimitris Papastamos /*
29609c6ecd3SDimitris Papastamos * This can be dangerous especially when we have clients such as
29709c6ecd3SDimitris Papastamos * PMICs, therefore don't provide any real compile time configuration option
29809c6ecd3SDimitris Papastamos * for this feature, people who want to use this will need to modify
29909c6ecd3SDimitris Papastamos * the source code directly.
30009c6ecd3SDimitris Papastamos */
regmap_map_write_file(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)30109c6ecd3SDimitris Papastamos static ssize_t regmap_map_write_file(struct file *file,
30209c6ecd3SDimitris Papastamos const char __user *user_buf,
30309c6ecd3SDimitris Papastamos size_t count, loff_t *ppos)
30409c6ecd3SDimitris Papastamos {
30509c6ecd3SDimitris Papastamos char buf[32];
30609c6ecd3SDimitris Papastamos size_t buf_size;
30709c6ecd3SDimitris Papastamos char *start = buf;
30809c6ecd3SDimitris Papastamos unsigned long reg, value;
30909c6ecd3SDimitris Papastamos struct regmap *map = file->private_data;
31068e850d8SDimitris Papastamos int ret;
31109c6ecd3SDimitris Papastamos
31209c6ecd3SDimitris Papastamos buf_size = min(count, (sizeof(buf)-1));
31309c6ecd3SDimitris Papastamos if (copy_from_user(buf, user_buf, buf_size))
31409c6ecd3SDimitris Papastamos return -EFAULT;
31509c6ecd3SDimitris Papastamos buf[buf_size] = 0;
31609c6ecd3SDimitris Papastamos
31709c6ecd3SDimitris Papastamos while (*start == ' ')
31809c6ecd3SDimitris Papastamos start++;
31909c6ecd3SDimitris Papastamos reg = simple_strtoul(start, &start, 16);
32009c6ecd3SDimitris Papastamos while (*start == ' ')
32109c6ecd3SDimitris Papastamos start++;
32234da5e67SJingoo Han if (kstrtoul(start, 16, &value))
32309c6ecd3SDimitris Papastamos return -EINVAL;
32409c6ecd3SDimitris Papastamos
32509c6ecd3SDimitris Papastamos /* Userspace has been fiddling around behind the kernel's back */
326f9e464a5SMark Brown add_taint(TAINT_USER, LOCKDEP_STILL_OK);
32709c6ecd3SDimitris Papastamos
32868e850d8SDimitris Papastamos ret = regmap_write(map, reg, value);
32968e850d8SDimitris Papastamos if (ret < 0)
33068e850d8SDimitris Papastamos return ret;
33109c6ecd3SDimitris Papastamos return buf_size;
33209c6ecd3SDimitris Papastamos }
33309c6ecd3SDimitris Papastamos #else
33409c6ecd3SDimitris Papastamos #define regmap_map_write_file NULL
33509c6ecd3SDimitris Papastamos #endif
33609c6ecd3SDimitris Papastamos
33731244e39SMark Brown static const struct file_operations regmap_map_fops = {
338234e3405SStephen Boyd .open = simple_open,
33931244e39SMark Brown .read = regmap_map_read_file,
34009c6ecd3SDimitris Papastamos .write = regmap_map_write_file,
34131244e39SMark Brown .llseek = default_llseek,
34231244e39SMark Brown };
34331244e39SMark Brown
regmap_range_read_file(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)3444b020b3fSMark Brown static ssize_t regmap_range_read_file(struct file *file, char __user *user_buf,
3454b020b3fSMark Brown size_t count, loff_t *ppos)
3464b020b3fSMark Brown {
3474b020b3fSMark Brown struct regmap_range_node *range = file->private_data;
3484b020b3fSMark Brown struct regmap *map = range->map;
3494b020b3fSMark Brown
3504b020b3fSMark Brown return regmap_read_debugfs(map, range->range_min, range->range_max,
3514b020b3fSMark Brown user_buf, count, ppos);
3524b020b3fSMark Brown }
3534b020b3fSMark Brown
3544b020b3fSMark Brown static const struct file_operations regmap_range_fops = {
3554b020b3fSMark Brown .open = simple_open,
3564b020b3fSMark Brown .read = regmap_range_read_file,
3574b020b3fSMark Brown .llseek = default_llseek,
3584b020b3fSMark Brown };
3594b020b3fSMark Brown
regmap_reg_ranges_read_file(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)360065b4c58SDimitris Papastamos static ssize_t regmap_reg_ranges_read_file(struct file *file,
361065b4c58SDimitris Papastamos char __user *user_buf, size_t count,
362065b4c58SDimitris Papastamos loff_t *ppos)
363065b4c58SDimitris Papastamos {
364065b4c58SDimitris Papastamos struct regmap *map = file->private_data;
365065b4c58SDimitris Papastamos struct regmap_debugfs_off_cache *c;
366065b4c58SDimitris Papastamos loff_t p = 0;
367065b4c58SDimitris Papastamos size_t buf_pos = 0;
368065b4c58SDimitris Papastamos char *buf;
369065b4c58SDimitris Papastamos char *entry;
370065b4c58SDimitris Papastamos int ret;
371d63aa09fSJinchao Wang unsigned int entry_len;
372065b4c58SDimitris Papastamos
373065b4c58SDimitris Papastamos if (*ppos < 0 || !count)
374065b4c58SDimitris Papastamos return -EINVAL;
375065b4c58SDimitris Papastamos
37623baf831SKirill A. Shutemov if (count > (PAGE_SIZE << MAX_ORDER))
37723baf831SKirill A. Shutemov count = PAGE_SIZE << MAX_ORDER;
37874edd08aSPeng Fan
379065b4c58SDimitris Papastamos buf = kmalloc(count, GFP_KERNEL);
380065b4c58SDimitris Papastamos if (!buf)
381065b4c58SDimitris Papastamos return -ENOMEM;
382065b4c58SDimitris Papastamos
383065b4c58SDimitris Papastamos entry = kmalloc(PAGE_SIZE, GFP_KERNEL);
384065b4c58SDimitris Papastamos if (!entry) {
385065b4c58SDimitris Papastamos kfree(buf);
386065b4c58SDimitris Papastamos return -ENOMEM;
387065b4c58SDimitris Papastamos }
388065b4c58SDimitris Papastamos
389065b4c58SDimitris Papastamos /* While we are at it, build the register dump cache
390065b4c58SDimitris Papastamos * now so the read() operation on the `registers' file
391065b4c58SDimitris Papastamos * can benefit from using the cache. We do not care
392065b4c58SDimitris Papastamos * about the file position information that is contained
393065b4c58SDimitris Papastamos * in the cache, just about the actual register blocks */
394065b4c58SDimitris Papastamos regmap_calc_tot_len(map, buf, count);
395065b4c58SDimitris Papastamos regmap_debugfs_get_dump_start(map, 0, *ppos, &p);
396065b4c58SDimitris Papastamos
397065b4c58SDimitris Papastamos /* Reset file pointer as the fixed-format of the `registers'
398065b4c58SDimitris Papastamos * file is not compatible with the `range' file */
399065b4c58SDimitris Papastamos p = 0;
400065b4c58SDimitris Papastamos mutex_lock(&map->cache_lock);
401065b4c58SDimitris Papastamos list_for_each_entry(c, &map->debugfs_off_cache, list) {
402ca07e9f3SRasmus Villemoes entry_len = snprintf(entry, PAGE_SIZE, "%x-%x\n",
403065b4c58SDimitris Papastamos c->base_reg, c->max_reg);
404065b4c58SDimitris Papastamos if (p >= *ppos) {
405ca07e9f3SRasmus Villemoes if (buf_pos + entry_len > count)
406065b4c58SDimitris Papastamos break;
40720991cdbSRasmus Villemoes memcpy(buf + buf_pos, entry, entry_len);
408e34dc490SRasmus Villemoes buf_pos += entry_len;
409065b4c58SDimitris Papastamos }
410ca07e9f3SRasmus Villemoes p += entry_len;
411065b4c58SDimitris Papastamos }
412065b4c58SDimitris Papastamos mutex_unlock(&map->cache_lock);
413065b4c58SDimitris Papastamos
414065b4c58SDimitris Papastamos kfree(entry);
415065b4c58SDimitris Papastamos ret = buf_pos;
416065b4c58SDimitris Papastamos
417065b4c58SDimitris Papastamos if (copy_to_user(user_buf, buf, buf_pos)) {
418065b4c58SDimitris Papastamos ret = -EFAULT;
419065b4c58SDimitris Papastamos goto out_buf;
420065b4c58SDimitris Papastamos }
421065b4c58SDimitris Papastamos
422065b4c58SDimitris Papastamos *ppos += buf_pos;
423065b4c58SDimitris Papastamos out_buf:
424065b4c58SDimitris Papastamos kfree(buf);
425065b4c58SDimitris Papastamos return ret;
426065b4c58SDimitris Papastamos }
427065b4c58SDimitris Papastamos
428065b4c58SDimitris Papastamos static const struct file_operations regmap_reg_ranges_fops = {
429065b4c58SDimitris Papastamos .open = simple_open,
430065b4c58SDimitris Papastamos .read = regmap_reg_ranges_read_file,
431065b4c58SDimitris Papastamos .llseek = default_llseek,
432065b4c58SDimitris Papastamos };
433065b4c58SDimitris Papastamos
regmap_access_show(struct seq_file * s,void * ignored)4348da61f24SMark Brown static int regmap_access_show(struct seq_file *s, void *ignored)
435449e3842SMark Brown {
4368da61f24SMark Brown struct regmap *map = s->private;
4378da61f24SMark Brown int i, reg_len;
438449e3842SMark Brown
4399ae3109dSMark Brown reg_len = regmap_calc_reg_len(map->max_register);
440449e3842SMark Brown
441f01ee60fSStephen Warren for (i = 0; i <= map->max_register; i += map->reg_stride) {
442449e3842SMark Brown /* Ignore registers which are neither readable nor writable */
443449e3842SMark Brown if (!regmap_readable(map, i) && !regmap_writeable(map, i))
444449e3842SMark Brown continue;
445449e3842SMark Brown
446449e3842SMark Brown /* Format the register */
4478da61f24SMark Brown seq_printf(s, "%.*x: %c %c %c %c\n", reg_len, i,
448449e3842SMark Brown regmap_readable(map, i) ? 'y' : 'n',
449449e3842SMark Brown regmap_writeable(map, i) ? 'y' : 'n',
450449e3842SMark Brown regmap_volatile(map, i) ? 'y' : 'n',
451449e3842SMark Brown regmap_precious(map, i) ? 'y' : 'n');
452449e3842SMark Brown }
453449e3842SMark Brown
4548da61f24SMark Brown return 0;
455449e3842SMark Brown }
456449e3842SMark Brown
457580d4857SYangtao Li DEFINE_SHOW_ATTRIBUTE(regmap_access);
45831244e39SMark Brown
regmap_cache_only_write_file(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)459d3dc5430SRichard Fitzgerald static ssize_t regmap_cache_only_write_file(struct file *file,
460d3dc5430SRichard Fitzgerald const char __user *user_buf,
461d3dc5430SRichard Fitzgerald size_t count, loff_t *ppos)
462d3dc5430SRichard Fitzgerald {
463d3dc5430SRichard Fitzgerald struct regmap *map = container_of(file->private_data,
464d3dc5430SRichard Fitzgerald struct regmap, cache_only);
465299632e5SDouglas Anderson bool new_val, require_sync = false;
466d3dc5430SRichard Fitzgerald int err;
467d3dc5430SRichard Fitzgerald
468299632e5SDouglas Anderson err = kstrtobool_from_user(user_buf, count, &new_val);
469299632e5SDouglas Anderson /* Ignore malforned data like debugfs_write_file_bool() */
470299632e5SDouglas Anderson if (err)
471299632e5SDouglas Anderson return count;
472299632e5SDouglas Anderson
473299632e5SDouglas Anderson err = debugfs_file_get(file->f_path.dentry);
474299632e5SDouglas Anderson if (err)
475299632e5SDouglas Anderson return err;
476299632e5SDouglas Anderson
477d3dc5430SRichard Fitzgerald map->lock(map->lock_arg);
478d3dc5430SRichard Fitzgerald
479299632e5SDouglas Anderson if (new_val && !map->cache_only) {
480d3dc5430SRichard Fitzgerald dev_warn(map->dev, "debugfs cache_only=Y forced\n");
481d3dc5430SRichard Fitzgerald add_taint(TAINT_USER, LOCKDEP_STILL_OK);
482299632e5SDouglas Anderson } else if (!new_val && map->cache_only) {
483d3dc5430SRichard Fitzgerald dev_warn(map->dev, "debugfs cache_only=N forced: syncing cache\n");
484d3dc5430SRichard Fitzgerald require_sync = true;
485d3dc5430SRichard Fitzgerald }
486299632e5SDouglas Anderson map->cache_only = new_val;
487d3dc5430SRichard Fitzgerald
488d3dc5430SRichard Fitzgerald map->unlock(map->lock_arg);
489299632e5SDouglas Anderson debugfs_file_put(file->f_path.dentry);
490d3dc5430SRichard Fitzgerald
491d3dc5430SRichard Fitzgerald if (require_sync) {
492d3dc5430SRichard Fitzgerald err = regcache_sync(map);
493d3dc5430SRichard Fitzgerald if (err)
494d3dc5430SRichard Fitzgerald dev_err(map->dev, "Failed to sync cache %d\n", err);
495d3dc5430SRichard Fitzgerald }
496d3dc5430SRichard Fitzgerald
497299632e5SDouglas Anderson return count;
498d3dc5430SRichard Fitzgerald }
499d3dc5430SRichard Fitzgerald
500d3dc5430SRichard Fitzgerald static const struct file_operations regmap_cache_only_fops = {
501d3dc5430SRichard Fitzgerald .open = simple_open,
502d3dc5430SRichard Fitzgerald .read = debugfs_read_file_bool,
503d3dc5430SRichard Fitzgerald .write = regmap_cache_only_write_file,
504d3dc5430SRichard Fitzgerald };
505d3dc5430SRichard Fitzgerald
regmap_cache_bypass_write_file(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)506d3dc5430SRichard Fitzgerald static ssize_t regmap_cache_bypass_write_file(struct file *file,
507d3dc5430SRichard Fitzgerald const char __user *user_buf,
508d3dc5430SRichard Fitzgerald size_t count, loff_t *ppos)
509d3dc5430SRichard Fitzgerald {
510d3dc5430SRichard Fitzgerald struct regmap *map = container_of(file->private_data,
511d3dc5430SRichard Fitzgerald struct regmap, cache_bypass);
512299632e5SDouglas Anderson bool new_val;
513299632e5SDouglas Anderson int err;
514299632e5SDouglas Anderson
515299632e5SDouglas Anderson err = kstrtobool_from_user(user_buf, count, &new_val);
516299632e5SDouglas Anderson /* Ignore malforned data like debugfs_write_file_bool() */
517299632e5SDouglas Anderson if (err)
518299632e5SDouglas Anderson return count;
519299632e5SDouglas Anderson
520299632e5SDouglas Anderson err = debugfs_file_get(file->f_path.dentry);
521299632e5SDouglas Anderson if (err)
522299632e5SDouglas Anderson return err;
523d3dc5430SRichard Fitzgerald
524d3dc5430SRichard Fitzgerald map->lock(map->lock_arg);
525d3dc5430SRichard Fitzgerald
526299632e5SDouglas Anderson if (new_val && !map->cache_bypass) {
527d3dc5430SRichard Fitzgerald dev_warn(map->dev, "debugfs cache_bypass=Y forced\n");
528d3dc5430SRichard Fitzgerald add_taint(TAINT_USER, LOCKDEP_STILL_OK);
529299632e5SDouglas Anderson } else if (!new_val && map->cache_bypass) {
530d3dc5430SRichard Fitzgerald dev_warn(map->dev, "debugfs cache_bypass=N forced\n");
531d3dc5430SRichard Fitzgerald }
532299632e5SDouglas Anderson map->cache_bypass = new_val;
533d3dc5430SRichard Fitzgerald
534d3dc5430SRichard Fitzgerald map->unlock(map->lock_arg);
535299632e5SDouglas Anderson debugfs_file_put(file->f_path.dentry);
536d3dc5430SRichard Fitzgerald
537299632e5SDouglas Anderson return count;
538d3dc5430SRichard Fitzgerald }
539d3dc5430SRichard Fitzgerald
540d3dc5430SRichard Fitzgerald static const struct file_operations regmap_cache_bypass_fops = {
541d3dc5430SRichard Fitzgerald .open = simple_open,
542d3dc5430SRichard Fitzgerald .read = debugfs_read_file_bool,
543d3dc5430SRichard Fitzgerald .write = regmap_cache_bypass_write_file,
544d3dc5430SRichard Fitzgerald };
545d3dc5430SRichard Fitzgerald
regmap_debugfs_init(struct regmap * map)54694cc89ebSCharles Keepax void regmap_debugfs_init(struct regmap *map)
54731244e39SMark Brown {
5484b020b3fSMark Brown struct rb_node *next;
5494b020b3fSMark Brown struct regmap_range_node *range_node;
5502c98e0c1SXiubo Li const char *devname = "dummy";
55194cc89ebSCharles Keepax const char *name = map->name;
5524b020b3fSMark Brown
553078711d7SBartosz Golaszewski /*
554078711d7SBartosz Golaszewski * Userspace can initiate reads from the hardware over debugfs.
555078711d7SBartosz Golaszewski * Normally internal regmap structures and buffers are protected with
556078711d7SBartosz Golaszewski * a mutex or a spinlock, but if the regmap owner decided to disable
557078711d7SBartosz Golaszewski * all locking mechanisms, this is no longer the case. For safety:
558078711d7SBartosz Golaszewski * don't create the debugfs entries if locking is disabled.
559078711d7SBartosz Golaszewski */
560a5ba91c3SBartosz Golaszewski if (map->debugfs_disable) {
561a5ba91c3SBartosz Golaszewski dev_dbg(map->dev, "regmap locking disabled - not creating debugfs entries\n");
56272465736SMark Brown return;
563a5ba91c3SBartosz Golaszewski }
56472465736SMark Brown
565a52eaeb1STero Kristo /* If we don't have the debugfs root yet, postpone init */
566a52eaeb1STero Kristo if (!regmap_debugfs_root) {
567a52eaeb1STero Kristo struct regmap_debugfs_node *node;
568a52eaeb1STero Kristo node = kzalloc(sizeof(*node), GFP_KERNEL);
569a52eaeb1STero Kristo if (!node)
570a52eaeb1STero Kristo return;
571a52eaeb1STero Kristo node->map = map;
572a52eaeb1STero Kristo mutex_lock(®map_debugfs_early_lock);
573a52eaeb1STero Kristo list_add(&node->link, ®map_debugfs_early_list);
574a52eaeb1STero Kristo mutex_unlock(®map_debugfs_early_lock);
575a52eaeb1STero Kristo return;
576a52eaeb1STero Kristo }
577a52eaeb1STero Kristo
5785166b7c0SMark Brown INIT_LIST_HEAD(&map->debugfs_off_cache);
579065b4c58SDimitris Papastamos mutex_init(&map->cache_lock);
5805166b7c0SMark Brown
5812c98e0c1SXiubo Li if (map->dev)
5822c98e0c1SXiubo Li devname = dev_name(map->dev);
5832c98e0c1SXiubo Li
584d3c242e1SStephen Warren if (name) {
585cffa4b21SXiaolei Wang if (!map->debugfs_name) {
586d3c242e1SStephen Warren map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
5872c98e0c1SXiubo Li devname, name);
588cffa4b21SXiaolei Wang if (!map->debugfs_name)
589cffa4b21SXiaolei Wang return;
590cffa4b21SXiaolei Wang }
591d3c242e1SStephen Warren name = map->debugfs_name;
592d3c242e1SStephen Warren } else {
5932c98e0c1SXiubo Li name = devname;
594d3c242e1SStephen Warren }
595d3c242e1SStephen Warren
596a430ab20SFabio Estevam if (!strcmp(name, "dummy")) {
5972899872bSDaniel Baluta kfree(map->debugfs_name);
59846589e9cSMark Brown map->debugfs_name = kasprintf(GFP_KERNEL, "dummy%d",
59946589e9cSMark Brown dummy_index);
600cffa4b21SXiaolei Wang if (!map->debugfs_name)
601cffa4b21SXiaolei Wang return;
60246589e9cSMark Brown name = map->debugfs_name;
603a430ab20SFabio Estevam dummy_index++;
604a430ab20SFabio Estevam }
605a430ab20SFabio Estevam
606d3c242e1SStephen Warren map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
60731244e39SMark Brown
608f0c2319fSDimitris Papastamos debugfs_create_file("name", 0400, map->debugfs,
609f0c2319fSDimitris Papastamos map, ®map_name_fops);
610f0c2319fSDimitris Papastamos
611065b4c58SDimitris Papastamos debugfs_create_file("range", 0400, map->debugfs,
612065b4c58SDimitris Papastamos map, ®map_reg_ranges_fops);
613065b4c58SDimitris Papastamos
614676970daSPawel Moll if (map->max_register || regmap_readable(map, 0)) {
615ffff7a12SMarkus Pargmann umode_t registers_mode;
616ffff7a12SMarkus Pargmann
6171635e888SAxel Lin #if defined(REGMAP_ALLOW_WRITE_DEBUGFS)
618ffff7a12SMarkus Pargmann registers_mode = 0600;
6191635e888SAxel Lin #else
620ffff7a12SMarkus Pargmann registers_mode = 0400;
6211635e888SAxel Lin #endif
622ffff7a12SMarkus Pargmann
623ffff7a12SMarkus Pargmann debugfs_create_file("registers", registers_mode, map->debugfs,
62431244e39SMark Brown map, ®map_map_fops);
625449e3842SMark Brown debugfs_create_file("access", 0400, map->debugfs,
626449e3842SMark Brown map, ®map_access_fops);
627449e3842SMark Brown }
628028a01e6SMark Brown
629028a01e6SMark Brown if (map->cache_type) {
630d3dc5430SRichard Fitzgerald debugfs_create_file("cache_only", 0600, map->debugfs,
631d3dc5430SRichard Fitzgerald &map->cache_only, ®map_cache_only_fops);
632028a01e6SMark Brown debugfs_create_bool("cache_dirty", 0400, map->debugfs,
633028a01e6SMark Brown &map->cache_dirty);
634d3dc5430SRichard Fitzgerald debugfs_create_file("cache_bypass", 0600, map->debugfs,
635d3dc5430SRichard Fitzgerald &map->cache_bypass,
636d3dc5430SRichard Fitzgerald ®map_cache_bypass_fops);
637028a01e6SMark Brown }
6384b020b3fSMark Brown
639b629c698SWaqar Hameed /*
640b629c698SWaqar Hameed * This could interfere with driver operation. Therefore, don't provide
641b629c698SWaqar Hameed * any real compile time configuration option for this feature. One will
642b629c698SWaqar Hameed * have to modify the source code directly in order to use it.
643b629c698SWaqar Hameed */
644b629c698SWaqar Hameed #undef REGMAP_ALLOW_FORCE_WRITE_FIELD_DEBUGFS
645b629c698SWaqar Hameed #ifdef REGMAP_ALLOW_FORCE_WRITE_FIELD_DEBUGFS
646b629c698SWaqar Hameed debugfs_create_bool("force_write_field", 0600, map->debugfs,
647b629c698SWaqar Hameed &map->force_write_field);
648b629c698SWaqar Hameed #endif
649b629c698SWaqar Hameed
6504b020b3fSMark Brown next = rb_first(&map->range_tree);
6514b020b3fSMark Brown while (next) {
6524b020b3fSMark Brown range_node = rb_entry(next, struct regmap_range_node, node);
6534b020b3fSMark Brown
6544b020b3fSMark Brown if (range_node->name)
6554b020b3fSMark Brown debugfs_create_file(range_node->name, 0400,
6564b020b3fSMark Brown map->debugfs, range_node,
6574b020b3fSMark Brown ®map_range_fops);
6584b020b3fSMark Brown
6594b020b3fSMark Brown next = rb_next(&range_node->node);
6604b020b3fSMark Brown }
6615e0cbe78SLars-Peter Clausen
6625e0cbe78SLars-Peter Clausen if (map->cache_ops && map->cache_ops->debugfs_init)
6635e0cbe78SLars-Peter Clausen map->cache_ops->debugfs_init(map);
66431244e39SMark Brown }
66531244e39SMark Brown
regmap_debugfs_exit(struct regmap * map)66631244e39SMark Brown void regmap_debugfs_exit(struct regmap *map)
66731244e39SMark Brown {
668a52eaeb1STero Kristo if (map->debugfs) {
66931244e39SMark Brown debugfs_remove_recursive(map->debugfs);
670065b4c58SDimitris Papastamos mutex_lock(&map->cache_lock);
67195f971c7SMark Brown regmap_debugfs_free_dump_cache(map);
672065b4c58SDimitris Papastamos mutex_unlock(&map->cache_lock);
673d3c242e1SStephen Warren kfree(map->debugfs_name);
674e41a962fSMeng Li map->debugfs_name = NULL;
675a52eaeb1STero Kristo } else {
676a52eaeb1STero Kristo struct regmap_debugfs_node *node, *tmp;
677a52eaeb1STero Kristo
678a52eaeb1STero Kristo mutex_lock(®map_debugfs_early_lock);
679a52eaeb1STero Kristo list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list,
680a52eaeb1STero Kristo link) {
681a52eaeb1STero Kristo if (node->map == map) {
682a52eaeb1STero Kristo list_del(&node->link);
683a52eaeb1STero Kristo kfree(node);
684a52eaeb1STero Kristo }
685a52eaeb1STero Kristo }
686a52eaeb1STero Kristo mutex_unlock(®map_debugfs_early_lock);
687a52eaeb1STero Kristo }
68831244e39SMark Brown }
68931244e39SMark Brown
regmap_debugfs_initcall(void)69031244e39SMark Brown void regmap_debugfs_initcall(void)
69131244e39SMark Brown {
692a52eaeb1STero Kristo struct regmap_debugfs_node *node, *tmp;
693a52eaeb1STero Kristo
69431244e39SMark Brown regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
695a52eaeb1STero Kristo
696a52eaeb1STero Kristo mutex_lock(®map_debugfs_early_lock);
697a52eaeb1STero Kristo list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list, link) {
69894cc89ebSCharles Keepax regmap_debugfs_init(node->map);
699a52eaeb1STero Kristo list_del(&node->link);
700a52eaeb1STero Kristo kfree(node);
701a52eaeb1STero Kristo }
702a52eaeb1STero Kristo mutex_unlock(®map_debugfs_early_lock);
70331244e39SMark Brown }
704