xref: /linux/drivers/base/regmap/regmap-debugfs.c (revision 580d4857)
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 
438*580d4857SYangtao Li DEFINE_SHOW_ATTRIBUTE(regmap_access);
43931244e39SMark Brown 
440d3dc5430SRichard Fitzgerald static ssize_t regmap_cache_only_write_file(struct file *file,
441d3dc5430SRichard Fitzgerald 					    const char __user *user_buf,
442d3dc5430SRichard Fitzgerald 					    size_t count, loff_t *ppos)
443d3dc5430SRichard Fitzgerald {
444d3dc5430SRichard Fitzgerald 	struct regmap *map = container_of(file->private_data,
445d3dc5430SRichard Fitzgerald 					  struct regmap, cache_only);
446d3dc5430SRichard Fitzgerald 	ssize_t result;
447d3dc5430SRichard Fitzgerald 	bool was_enabled, require_sync = false;
448d3dc5430SRichard Fitzgerald 	int err;
449d3dc5430SRichard Fitzgerald 
450d3dc5430SRichard Fitzgerald 	map->lock(map->lock_arg);
451d3dc5430SRichard Fitzgerald 
452d3dc5430SRichard Fitzgerald 	was_enabled = map->cache_only;
453d3dc5430SRichard Fitzgerald 
454d3dc5430SRichard Fitzgerald 	result = debugfs_write_file_bool(file, user_buf, count, ppos);
455d3dc5430SRichard Fitzgerald 	if (result < 0) {
456d3dc5430SRichard Fitzgerald 		map->unlock(map->lock_arg);
457d3dc5430SRichard Fitzgerald 		return result;
458d3dc5430SRichard Fitzgerald 	}
459d3dc5430SRichard Fitzgerald 
460d3dc5430SRichard Fitzgerald 	if (map->cache_only && !was_enabled) {
461d3dc5430SRichard Fitzgerald 		dev_warn(map->dev, "debugfs cache_only=Y forced\n");
462d3dc5430SRichard Fitzgerald 		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
463d3dc5430SRichard Fitzgerald 	} else if (!map->cache_only && was_enabled) {
464d3dc5430SRichard Fitzgerald 		dev_warn(map->dev, "debugfs cache_only=N forced: syncing cache\n");
465d3dc5430SRichard Fitzgerald 		require_sync = true;
466d3dc5430SRichard Fitzgerald 	}
467d3dc5430SRichard Fitzgerald 
468d3dc5430SRichard Fitzgerald 	map->unlock(map->lock_arg);
469d3dc5430SRichard Fitzgerald 
470d3dc5430SRichard Fitzgerald 	if (require_sync) {
471d3dc5430SRichard Fitzgerald 		err = regcache_sync(map);
472d3dc5430SRichard Fitzgerald 		if (err)
473d3dc5430SRichard Fitzgerald 			dev_err(map->dev, "Failed to sync cache %d\n", err);
474d3dc5430SRichard Fitzgerald 	}
475d3dc5430SRichard Fitzgerald 
476d3dc5430SRichard Fitzgerald 	return result;
477d3dc5430SRichard Fitzgerald }
478d3dc5430SRichard Fitzgerald 
479d3dc5430SRichard Fitzgerald static const struct file_operations regmap_cache_only_fops = {
480d3dc5430SRichard Fitzgerald 	.open = simple_open,
481d3dc5430SRichard Fitzgerald 	.read = debugfs_read_file_bool,
482d3dc5430SRichard Fitzgerald 	.write = regmap_cache_only_write_file,
483d3dc5430SRichard Fitzgerald };
484d3dc5430SRichard Fitzgerald 
485d3dc5430SRichard Fitzgerald static ssize_t regmap_cache_bypass_write_file(struct file *file,
486d3dc5430SRichard Fitzgerald 					      const char __user *user_buf,
487d3dc5430SRichard Fitzgerald 					      size_t count, loff_t *ppos)
488d3dc5430SRichard Fitzgerald {
489d3dc5430SRichard Fitzgerald 	struct regmap *map = container_of(file->private_data,
490d3dc5430SRichard Fitzgerald 					  struct regmap, cache_bypass);
491d3dc5430SRichard Fitzgerald 	ssize_t result;
492d3dc5430SRichard Fitzgerald 	bool was_enabled;
493d3dc5430SRichard Fitzgerald 
494d3dc5430SRichard Fitzgerald 	map->lock(map->lock_arg);
495d3dc5430SRichard Fitzgerald 
496d3dc5430SRichard Fitzgerald 	was_enabled = map->cache_bypass;
497d3dc5430SRichard Fitzgerald 
498d3dc5430SRichard Fitzgerald 	result = debugfs_write_file_bool(file, user_buf, count, ppos);
499d3dc5430SRichard Fitzgerald 	if (result < 0)
500d3dc5430SRichard Fitzgerald 		goto out;
501d3dc5430SRichard Fitzgerald 
502d3dc5430SRichard Fitzgerald 	if (map->cache_bypass && !was_enabled) {
503d3dc5430SRichard Fitzgerald 		dev_warn(map->dev, "debugfs cache_bypass=Y forced\n");
504d3dc5430SRichard Fitzgerald 		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
505d3dc5430SRichard Fitzgerald 	} else if (!map->cache_bypass && was_enabled) {
506d3dc5430SRichard Fitzgerald 		dev_warn(map->dev, "debugfs cache_bypass=N forced\n");
507d3dc5430SRichard Fitzgerald 	}
508d3dc5430SRichard Fitzgerald 
509d3dc5430SRichard Fitzgerald out:
510d3dc5430SRichard Fitzgerald 	map->unlock(map->lock_arg);
511d3dc5430SRichard Fitzgerald 
512d3dc5430SRichard Fitzgerald 	return result;
513d3dc5430SRichard Fitzgerald }
514d3dc5430SRichard Fitzgerald 
515d3dc5430SRichard Fitzgerald static const struct file_operations regmap_cache_bypass_fops = {
516d3dc5430SRichard Fitzgerald 	.open = simple_open,
517d3dc5430SRichard Fitzgerald 	.read = debugfs_read_file_bool,
518d3dc5430SRichard Fitzgerald 	.write = regmap_cache_bypass_write_file,
519d3dc5430SRichard Fitzgerald };
520d3dc5430SRichard Fitzgerald 
521d3c242e1SStephen Warren void regmap_debugfs_init(struct regmap *map, const char *name)
52231244e39SMark Brown {
5234b020b3fSMark Brown 	struct rb_node *next;
5244b020b3fSMark Brown 	struct regmap_range_node *range_node;
5252c98e0c1SXiubo Li 	const char *devname = "dummy";
5264b020b3fSMark Brown 
527078711d7SBartosz Golaszewski 	/*
528078711d7SBartosz Golaszewski 	 * Userspace can initiate reads from the hardware over debugfs.
529078711d7SBartosz Golaszewski 	 * Normally internal regmap structures and buffers are protected with
530078711d7SBartosz Golaszewski 	 * a mutex or a spinlock, but if the regmap owner decided to disable
531078711d7SBartosz Golaszewski 	 * all locking mechanisms, this is no longer the case. For safety:
532078711d7SBartosz Golaszewski 	 * don't create the debugfs entries if locking is disabled.
533078711d7SBartosz Golaszewski 	 */
534a5ba91c3SBartosz Golaszewski 	if (map->debugfs_disable) {
535a5ba91c3SBartosz Golaszewski 		dev_dbg(map->dev, "regmap locking disabled - not creating debugfs entries\n");
53672465736SMark Brown 		return;
537a5ba91c3SBartosz Golaszewski 	}
53872465736SMark Brown 
539a52eaeb1STero Kristo 	/* If we don't have the debugfs root yet, postpone init */
540a52eaeb1STero Kristo 	if (!regmap_debugfs_root) {
541a52eaeb1STero Kristo 		struct regmap_debugfs_node *node;
542a52eaeb1STero Kristo 		node = kzalloc(sizeof(*node), GFP_KERNEL);
543a52eaeb1STero Kristo 		if (!node)
544a52eaeb1STero Kristo 			return;
545a52eaeb1STero Kristo 		node->map = map;
546a52eaeb1STero Kristo 		node->name = name;
547a52eaeb1STero Kristo 		mutex_lock(&regmap_debugfs_early_lock);
548a52eaeb1STero Kristo 		list_add(&node->link, &regmap_debugfs_early_list);
549a52eaeb1STero Kristo 		mutex_unlock(&regmap_debugfs_early_lock);
550a52eaeb1STero Kristo 		return;
551a52eaeb1STero Kristo 	}
552a52eaeb1STero Kristo 
5535166b7c0SMark Brown 	INIT_LIST_HEAD(&map->debugfs_off_cache);
554065b4c58SDimitris Papastamos 	mutex_init(&map->cache_lock);
5555166b7c0SMark Brown 
5562c98e0c1SXiubo Li 	if (map->dev)
5572c98e0c1SXiubo Li 		devname = dev_name(map->dev);
5582c98e0c1SXiubo Li 
559d3c242e1SStephen Warren 	if (name) {
560d3c242e1SStephen Warren 		map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
5612c98e0c1SXiubo Li 					      devname, name);
562d3c242e1SStephen Warren 		name = map->debugfs_name;
563d3c242e1SStephen Warren 	} else {
5642c98e0c1SXiubo Li 		name = devname;
565d3c242e1SStephen Warren 	}
566d3c242e1SStephen Warren 
567a430ab20SFabio Estevam 	if (!strcmp(name, "dummy")) {
56846589e9cSMark Brown 		map->debugfs_name = kasprintf(GFP_KERNEL, "dummy%d",
56946589e9cSMark Brown 						dummy_index);
57046589e9cSMark Brown 		name = map->debugfs_name;
571a430ab20SFabio Estevam 		dummy_index++;
572a430ab20SFabio Estevam 	}
573a430ab20SFabio Estevam 
574d3c242e1SStephen Warren 	map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
57531244e39SMark Brown 	if (!map->debugfs) {
57659dd2a85SFabio Estevam 		dev_warn(map->dev,
57759dd2a85SFabio Estevam 			 "Failed to create %s debugfs directory\n", name);
57817cf46cfSJeffy Chen 
57917cf46cfSJeffy Chen 		kfree(map->debugfs_name);
58017cf46cfSJeffy Chen 		map->debugfs_name = NULL;
58131244e39SMark Brown 		return;
58231244e39SMark Brown 	}
58331244e39SMark Brown 
584f0c2319fSDimitris Papastamos 	debugfs_create_file("name", 0400, map->debugfs,
585f0c2319fSDimitris Papastamos 			    map, &regmap_name_fops);
586f0c2319fSDimitris Papastamos 
587065b4c58SDimitris Papastamos 	debugfs_create_file("range", 0400, map->debugfs,
588065b4c58SDimitris Papastamos 			    map, &regmap_reg_ranges_fops);
589065b4c58SDimitris Papastamos 
590676970daSPawel Moll 	if (map->max_register || regmap_readable(map, 0)) {
591ffff7a12SMarkus Pargmann 		umode_t registers_mode;
592ffff7a12SMarkus Pargmann 
5931635e888SAxel Lin #if defined(REGMAP_ALLOW_WRITE_DEBUGFS)
594ffff7a12SMarkus Pargmann 		registers_mode = 0600;
5951635e888SAxel Lin #else
596ffff7a12SMarkus Pargmann 		registers_mode = 0400;
5971635e888SAxel Lin #endif
598ffff7a12SMarkus Pargmann 
599ffff7a12SMarkus Pargmann 		debugfs_create_file("registers", registers_mode, map->debugfs,
60031244e39SMark Brown 				    map, &regmap_map_fops);
601449e3842SMark Brown 		debugfs_create_file("access", 0400, map->debugfs,
602449e3842SMark Brown 				    map, &regmap_access_fops);
603449e3842SMark Brown 	}
604028a01e6SMark Brown 
605028a01e6SMark Brown 	if (map->cache_type) {
606d3dc5430SRichard Fitzgerald 		debugfs_create_file("cache_only", 0600, map->debugfs,
607d3dc5430SRichard Fitzgerald 				    &map->cache_only, &regmap_cache_only_fops);
608028a01e6SMark Brown 		debugfs_create_bool("cache_dirty", 0400, map->debugfs,
609028a01e6SMark Brown 				    &map->cache_dirty);
610d3dc5430SRichard Fitzgerald 		debugfs_create_file("cache_bypass", 0600, map->debugfs,
611d3dc5430SRichard Fitzgerald 				    &map->cache_bypass,
612d3dc5430SRichard Fitzgerald 				    &regmap_cache_bypass_fops);
613028a01e6SMark Brown 	}
6144b020b3fSMark Brown 
6154b020b3fSMark Brown 	next = rb_first(&map->range_tree);
6164b020b3fSMark Brown 	while (next) {
6174b020b3fSMark Brown 		range_node = rb_entry(next, struct regmap_range_node, node);
6184b020b3fSMark Brown 
6194b020b3fSMark Brown 		if (range_node->name)
6204b020b3fSMark Brown 			debugfs_create_file(range_node->name, 0400,
6214b020b3fSMark Brown 					    map->debugfs, range_node,
6224b020b3fSMark Brown 					    &regmap_range_fops);
6234b020b3fSMark Brown 
6244b020b3fSMark Brown 		next = rb_next(&range_node->node);
6254b020b3fSMark Brown 	}
6265e0cbe78SLars-Peter Clausen 
6275e0cbe78SLars-Peter Clausen 	if (map->cache_ops && map->cache_ops->debugfs_init)
6285e0cbe78SLars-Peter Clausen 		map->cache_ops->debugfs_init(map);
62931244e39SMark Brown }
63031244e39SMark Brown 
63131244e39SMark Brown void regmap_debugfs_exit(struct regmap *map)
63231244e39SMark Brown {
633a52eaeb1STero Kristo 	if (map->debugfs) {
63431244e39SMark Brown 		debugfs_remove_recursive(map->debugfs);
635065b4c58SDimitris Papastamos 		mutex_lock(&map->cache_lock);
63695f971c7SMark Brown 		regmap_debugfs_free_dump_cache(map);
637065b4c58SDimitris Papastamos 		mutex_unlock(&map->cache_lock);
638d3c242e1SStephen Warren 		kfree(map->debugfs_name);
639a52eaeb1STero Kristo 	} else {
640a52eaeb1STero Kristo 		struct regmap_debugfs_node *node, *tmp;
641a52eaeb1STero Kristo 
642a52eaeb1STero Kristo 		mutex_lock(&regmap_debugfs_early_lock);
643a52eaeb1STero Kristo 		list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list,
644a52eaeb1STero Kristo 					 link) {
645a52eaeb1STero Kristo 			if (node->map == map) {
646a52eaeb1STero Kristo 				list_del(&node->link);
647a52eaeb1STero Kristo 				kfree(node);
648a52eaeb1STero Kristo 			}
649a52eaeb1STero Kristo 		}
650a52eaeb1STero Kristo 		mutex_unlock(&regmap_debugfs_early_lock);
651a52eaeb1STero Kristo 	}
65231244e39SMark Brown }
65331244e39SMark Brown 
65431244e39SMark Brown void regmap_debugfs_initcall(void)
65531244e39SMark Brown {
656a52eaeb1STero Kristo 	struct regmap_debugfs_node *node, *tmp;
657a52eaeb1STero Kristo 
65831244e39SMark Brown 	regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
65931244e39SMark Brown 	if (!regmap_debugfs_root) {
66031244e39SMark Brown 		pr_warn("regmap: Failed to create debugfs root\n");
66131244e39SMark Brown 		return;
66231244e39SMark Brown 	}
663a52eaeb1STero Kristo 
664a52eaeb1STero Kristo 	mutex_lock(&regmap_debugfs_early_lock);
665a52eaeb1STero Kristo 	list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list, link) {
666a52eaeb1STero Kristo 		regmap_debugfs_init(node->map, node->name);
667a52eaeb1STero Kristo 		list_del(&node->link);
668a52eaeb1STero Kristo 		kfree(node);
669a52eaeb1STero Kristo 	}
670a52eaeb1STero Kristo 	mutex_unlock(&regmap_debugfs_early_lock);
67131244e39SMark Brown }
672