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(&regmap_debugfs_early_lock);
573a52eaeb1STero Kristo 		list_add(&node->link, &regmap_debugfs_early_list);
574a52eaeb1STero Kristo 		mutex_unlock(&regmap_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, &regmap_name_fops);
610f0c2319fSDimitris Papastamos 
611065b4c58SDimitris Papastamos 	debugfs_create_file("range", 0400, map->debugfs,
612065b4c58SDimitris Papastamos 			    map, &regmap_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, &regmap_map_fops);
625449e3842SMark Brown 		debugfs_create_file("access", 0400, map->debugfs,
626449e3842SMark Brown 				    map, &regmap_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, &regmap_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 				    &regmap_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 					    &regmap_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(&regmap_debugfs_early_lock);
679a52eaeb1STero Kristo 		list_for_each_entry_safe(node, tmp, &regmap_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(&regmap_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(&regmap_debugfs_early_lock);
697a52eaeb1STero Kristo 	list_for_each_entry_safe(node, tmp, &regmap_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(&regmap_debugfs_early_lock);
70331244e39SMark Brown }
704