Blame view

drivers/base/regmap/regmap-debugfs.c 15.7 KB
31244e396   Mark Brown   regmap: Provide r...
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * Register map access API - debugfs
   *
   * Copyright 2011 Wolfson Microelectronics plc
   *
   * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
  
  #include <linux/slab.h>
31244e396   Mark Brown   regmap: Provide r...
14
15
16
  #include <linux/mutex.h>
  #include <linux/debugfs.h>
  #include <linux/uaccess.h>
51990e825   Paul Gortmaker   device.h: cleanup...
17
  #include <linux/device.h>
a52eaeb18   Tero Kristo   regmap: debugfs: ...
18
  #include <linux/list.h>
31244e396   Mark Brown   regmap: Provide r...
19
20
  
  #include "internal.h"
a52eaeb18   Tero Kristo   regmap: debugfs: ...
21
22
23
24
25
  struct regmap_debugfs_node {
  	struct regmap *map;
  	const char *name;
  	struct list_head link;
  };
31244e396   Mark Brown   regmap: Provide r...
26
  static struct dentry *regmap_debugfs_root;
a52eaeb18   Tero Kristo   regmap: debugfs: ...
27
28
  static LIST_HEAD(regmap_debugfs_early_list);
  static DEFINE_MUTEX(regmap_debugfs_early_lock);
31244e396   Mark Brown   regmap: Provide r...
29

21f555445   Mark Brown   regmap: Share som...
30
  /* Calculate the length of a fixed format  */
9ae3109d1   Mark Brown   regmap: debugfs: ...
31
  static size_t regmap_calc_reg_len(int max_val)
21f555445   Mark Brown   regmap: Share som...
32
  {
176fc2d57   Mark Brown   regmap: debugfs: ...
33
  	return snprintf(NULL, 0, "%x", max_val);
21f555445   Mark Brown   regmap: Share som...
34
  }
f0c2319f9   Dimitris Papastamos   regmap: Expose th...
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  static ssize_t regmap_name_read_file(struct file *file,
  				     char __user *user_buf, size_t count,
  				     loff_t *ppos)
  {
  	struct regmap *map = file->private_data;
  	int ret;
  	char *buf;
  
  	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
  	if (!buf)
  		return -ENOMEM;
  
  	ret = snprintf(buf, PAGE_SIZE, "%s
  ", map->dev->driver->name);
  	if (ret < 0) {
  		kfree(buf);
  		return ret;
  	}
  
  	ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
  	kfree(buf);
  	return ret;
  }
  
  static const struct file_operations regmap_name_fops = {
234e34058   Stephen Boyd   simple_open: auto...
60
  	.open = simple_open,
f0c2319f9   Dimitris Papastamos   regmap: Expose th...
61
62
63
  	.read = regmap_name_read_file,
  	.llseek = default_llseek,
  };
95f971c74   Mark Brown   regmap: debugfs: ...
64
65
66
67
68
69
70
71
72
73
74
75
  static void regmap_debugfs_free_dump_cache(struct regmap *map)
  {
  	struct regmap_debugfs_off_cache *c;
  
  	while (!list_empty(&map->debugfs_off_cache)) {
  		c = list_first_entry(&map->debugfs_off_cache,
  				     struct regmap_debugfs_off_cache,
  				     list);
  		list_del(&c->list);
  		kfree(c);
  	}
  }
359a2f176   Cristian Birsan   regmap: debugfs: ...
76
77
78
79
80
81
82
83
84
85
  static bool regmap_printable(struct regmap *map, unsigned int reg)
  {
  	if (regmap_precious(map, reg))
  		return false;
  
  	if (!regmap_readable(map, reg) && !regmap_cached(map, reg))
  		return false;
  
  	return true;
  }
afab2f7b2   Mark Brown   regmap: debugfs: ...
86
87
88
89
90
91
92
93
94
  /*
   * Work out where the start offset maps into register numbers, bearing
   * in mind that we suppress hidden registers.
   */
  static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
  						  unsigned int base,
  						  loff_t from,
  						  loff_t *pos)
  {
5166b7c00   Mark Brown   regmap: debugfs: ...
95
96
97
  	struct regmap_debugfs_off_cache *c = NULL;
  	loff_t p = 0;
  	unsigned int i, ret;
c2c1ee660   Dimitris Papastamos   regmap: debugfs: ...
98
99
  	unsigned int fpos_offset;
  	unsigned int reg_offset;
5166b7c00   Mark Brown   regmap: debugfs: ...
100

d6814a7da   Mark Brown   regmap: debugfs: ...
101
  	/* Suppress the cache if we're using a subrange */
26ee47411   Lars-Peter Clausen   regmap: debugfs: ...
102
103
  	if (base)
  		return base;
d6814a7da   Mark Brown   regmap: debugfs: ...
104

5166b7c00   Mark Brown   regmap: debugfs: ...
105
106
107
108
  	/*
  	 * If we don't have a cache build one so we don't have to do a
  	 * linear scan each time.
  	 */
065b4c587   Dimitris Papastamos   regmap: debugfs: ...
109
  	mutex_lock(&map->cache_lock);
480738de0   Dimitris Papastamos   regmap: debugfs: ...
110
  	i = base;
5166b7c00   Mark Brown   regmap: debugfs: ...
111
  	if (list_empty(&map->debugfs_off_cache)) {
480738de0   Dimitris Papastamos   regmap: debugfs: ...
112
  		for (; i <= map->max_register; i += map->reg_stride) {
5166b7c00   Mark Brown   regmap: debugfs: ...
113
  			/* Skip unprinted registers, closing off cache entry */
359a2f176   Cristian Birsan   regmap: debugfs: ...
114
  			if (!regmap_printable(map, i)) {
5166b7c00   Mark Brown   regmap: debugfs: ...
115
116
  				if (c) {
  					c->max = p - 1;
480738de0   Dimitris Papastamos   regmap: debugfs: ...
117
  					c->max_reg = i - map->reg_stride;
5166b7c00   Mark Brown   regmap: debugfs: ...
118
119
120
121
122
123
124
125
126
127
128
  					list_add_tail(&c->list,
  						      &map->debugfs_off_cache);
  					c = NULL;
  				}
  
  				continue;
  			}
  
  			/* No cache entry?  Start a new one */
  			if (!c) {
  				c = kzalloc(sizeof(*c), GFP_KERNEL);
95f971c74   Mark Brown   regmap: debugfs: ...
129
130
  				if (!c) {
  					regmap_debugfs_free_dump_cache(map);
065b4c587   Dimitris Papastamos   regmap: debugfs: ...
131
  					mutex_unlock(&map->cache_lock);
95f971c74   Mark Brown   regmap: debugfs: ...
132
133
  					return base;
  				}
5166b7c00   Mark Brown   regmap: debugfs: ...
134
135
136
137
138
139
140
  				c->min = p;
  				c->base_reg = i;
  			}
  
  			p += map->debugfs_tot_len;
  		}
  	}
afab2f7b2   Mark Brown   regmap: debugfs: ...
141

e8d6539c8   Mark Brown   regmap: debugfs: ...
142
143
144
  	/* Close the last entry off if we didn't scan beyond it */
  	if (c) {
  		c->max = p - 1;
480738de0   Dimitris Papastamos   regmap: debugfs: ...
145
  		c->max_reg = i - map->reg_stride;
e8d6539c8   Mark Brown   regmap: debugfs: ...
146
147
  		list_add_tail(&c->list,
  			      &map->debugfs_off_cache);
e8d6539c8   Mark Brown   regmap: debugfs: ...
148
  	}
5bd9f4bb3   Mark Brown   regmap: debugfs: ...
149
150
151
152
153
  	/*
  	 * This should never happen; we return above if we fail to
  	 * allocate and we should never be in this code if there are
  	 * no registers at all.
  	 */
a3471469b   Russell King   regmap: regmap: a...
154
155
  	WARN_ON(list_empty(&map->debugfs_off_cache));
  	ret = base;
5bd9f4bb3   Mark Brown   regmap: debugfs: ...
156

cf57d6071   Dimitris Papastamos   regmap: debugfs: ...
157
  	/* Find the relevant block:offset */
5166b7c00   Mark Brown   regmap: debugfs: ...
158
  	list_for_each_entry(c, &map->debugfs_off_cache, list) {
5a1d6d172   Mark Brown   regmap: debugfs: ...
159
  		if (from >= c->min && from <= c->max) {
cf57d6071   Dimitris Papastamos   regmap: debugfs: ...
160
161
162
  			fpos_offset = from - c->min;
  			reg_offset = fpos_offset / map->debugfs_tot_len;
  			*pos = c->min + (reg_offset * map->debugfs_tot_len);
065b4c587   Dimitris Papastamos   regmap: debugfs: ...
163
  			mutex_unlock(&map->cache_lock);
213fa5d96   Srinivas Kandagatla   regmap: debugfs: ...
164
  			return c->base_reg + (reg_offset * map->reg_stride);
afab2f7b2   Mark Brown   regmap: debugfs: ...
165
  		}
cf57d6071   Dimitris Papastamos   regmap: debugfs: ...
166
167
  		*pos = c->max;
  		ret = c->max_reg;
afab2f7b2   Mark Brown   regmap: debugfs: ...
168
  	}
065b4c587   Dimitris Papastamos   regmap: debugfs: ...
169
  	mutex_unlock(&map->cache_lock);
afab2f7b2   Mark Brown   regmap: debugfs: ...
170

5166b7c00   Mark Brown   regmap: debugfs: ...
171
  	return ret;
afab2f7b2   Mark Brown   regmap: debugfs: ...
172
  }
4dd7c5531   Dimitris Papastamos   regmap: debugfs: ...
173
174
175
176
177
  static inline void regmap_calc_tot_len(struct regmap *map,
  				       void *buf, size_t count)
  {
  	/* Calculate the length of a fixed format  */
  	if (!map->debugfs_tot_len) {
9ae3109d1   Mark Brown   regmap: debugfs: ...
178
  		map->debugfs_reg_len = regmap_calc_reg_len(map->max_register),
4dd7c5531   Dimitris Papastamos   regmap: debugfs: ...
179
180
181
182
183
184
  		map->debugfs_val_len = 2 * map->format.val_bytes;
  		map->debugfs_tot_len = map->debugfs_reg_len +
  			map->debugfs_val_len + 3;      /* : 
   */
  	}
  }
bd9cc12f4   Mark Brown   regmap: Factor ou...
185
186
187
  static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
  				   unsigned int to, char __user *user_buf,
  				   size_t count, loff_t *ppos)
31244e396   Mark Brown   regmap: Provide r...
188
  {
31244e396   Mark Brown   regmap: Provide r...
189
  	size_t buf_pos = 0;
afab2f7b2   Mark Brown   regmap: debugfs: ...
190
  	loff_t p = *ppos;
31244e396   Mark Brown   regmap: Provide r...
191
192
  	ssize_t ret;
  	int i;
31244e396   Mark Brown   regmap: Provide r...
193
  	char *buf;
afab2f7b2   Mark Brown   regmap: debugfs: ...
194
  	unsigned int val, start_reg;
31244e396   Mark Brown   regmap: Provide r...
195
196
197
198
199
200
201
  
  	if (*ppos < 0 || !count)
  		return -EINVAL;
  
  	buf = kmalloc(count, GFP_KERNEL);
  	if (!buf)
  		return -ENOMEM;
4dd7c5531   Dimitris Papastamos   regmap: debugfs: ...
202
  	regmap_calc_tot_len(map, buf, count);
31244e396   Mark Brown   regmap: Provide r...
203

afab2f7b2   Mark Brown   regmap: debugfs: ...
204
205
206
207
  	/* Work out which register we're starting at */
  	start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p);
  
  	for (i = start_reg; i <= to; i += map->reg_stride) {
359a2f176   Cristian Birsan   regmap: debugfs: ...
208
  		if (!regmap_readable(map, i) && !regmap_cached(map, i))
31244e396   Mark Brown   regmap: Provide r...
209
  			continue;
8de2f081e   Mark Brown   regmap: Add funct...
210
  		if (regmap_precious(map, i))
2efe1642b   Mark Brown   regmap: Skip prec...
211
  			continue;
31244e396   Mark Brown   regmap: Provide r...
212
213
214
  		/* If we're in the region the user is trying to read */
  		if (p >= *ppos) {
  			/* ...but not beyond it */
f3eb83994   Dimitris Papastamos   regmap: debugfs: ...
215
  			if (buf_pos + map->debugfs_tot_len > count)
31244e396   Mark Brown   regmap: Provide r...
216
217
218
219
  				break;
  
  			/* Format the register */
  			snprintf(buf + buf_pos, count - buf_pos, "%.*x: ",
cbc1938ba   Mark Brown   regmap: Cache reg...
220
221
  				 map->debugfs_reg_len, i - from);
  			buf_pos += map->debugfs_reg_len + 2;
31244e396   Mark Brown   regmap: Provide r...
222
223
224
225
226
  
  			/* Format the value, write all X if we can't read */
  			ret = regmap_read(map, i, &val);
  			if (ret == 0)
  				snprintf(buf + buf_pos, count - buf_pos,
cbc1938ba   Mark Brown   regmap: Cache reg...
227
  					 "%.*x", map->debugfs_val_len, val);
31244e396   Mark Brown   regmap: Provide r...
228
  			else
cbc1938ba   Mark Brown   regmap: Cache reg...
229
230
  				memset(buf + buf_pos, 'X',
  				       map->debugfs_val_len);
31244e396   Mark Brown   regmap: Provide r...
231
232
233
234
235
  			buf_pos += 2 * map->format.val_bytes;
  
  			buf[buf_pos++] = '
  ';
  		}
cbc1938ba   Mark Brown   regmap: Cache reg...
236
  		p += map->debugfs_tot_len;
31244e396   Mark Brown   regmap: Provide r...
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
  	}
  
  	ret = buf_pos;
  
  	if (copy_to_user(user_buf, buf, buf_pos)) {
  		ret = -EFAULT;
  		goto out;
  	}
  
  	*ppos += buf_pos;
  
  out:
  	kfree(buf);
  	return ret;
  }
bd9cc12f4   Mark Brown   regmap: Factor ou...
252
253
254
255
256
257
258
259
  static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
  				    size_t count, loff_t *ppos)
  {
  	struct regmap *map = file->private_data;
  
  	return regmap_read_debugfs(map, 0, map->max_register, user_buf,
  				   count, ppos);
  }
09c6ecd39   Dimitris Papastamos   regmap: Add suppo...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
  #undef REGMAP_ALLOW_WRITE_DEBUGFS
  #ifdef REGMAP_ALLOW_WRITE_DEBUGFS
  /*
   * This can be dangerous especially when we have clients such as
   * PMICs, therefore don't provide any real compile time configuration option
   * for this feature, people who want to use this will need to modify
   * the source code directly.
   */
  static ssize_t regmap_map_write_file(struct file *file,
  				     const char __user *user_buf,
  				     size_t count, loff_t *ppos)
  {
  	char buf[32];
  	size_t buf_size;
  	char *start = buf;
  	unsigned long reg, value;
  	struct regmap *map = file->private_data;
68e850d80   Dimitris Papastamos   regmap: debugfs: ...
277
  	int ret;
09c6ecd39   Dimitris Papastamos   regmap: Add suppo...
278
279
280
281
282
283
284
285
286
287
288
  
  	buf_size = min(count, (sizeof(buf)-1));
  	if (copy_from_user(buf, user_buf, buf_size))
  		return -EFAULT;
  	buf[buf_size] = 0;
  
  	while (*start == ' ')
  		start++;
  	reg = simple_strtoul(start, &start, 16);
  	while (*start == ' ')
  		start++;
34da5e677   Jingoo Han   driver core: repl...
289
  	if (kstrtoul(start, 16, &value))
09c6ecd39   Dimitris Papastamos   regmap: Add suppo...
290
291
292
  		return -EINVAL;
  
  	/* Userspace has been fiddling around behind the kernel's back */
f9e464a56   Mark Brown   regmap: debugfs: ...
293
  	add_taint(TAINT_USER, LOCKDEP_STILL_OK);
09c6ecd39   Dimitris Papastamos   regmap: Add suppo...
294

68e850d80   Dimitris Papastamos   regmap: debugfs: ...
295
296
297
  	ret = regmap_write(map, reg, value);
  	if (ret < 0)
  		return ret;
09c6ecd39   Dimitris Papastamos   regmap: Add suppo...
298
299
300
301
302
  	return buf_size;
  }
  #else
  #define regmap_map_write_file NULL
  #endif
31244e396   Mark Brown   regmap: Provide r...
303
  static const struct file_operations regmap_map_fops = {
234e34058   Stephen Boyd   simple_open: auto...
304
  	.open = simple_open,
31244e396   Mark Brown   regmap: Provide r...
305
  	.read = regmap_map_read_file,
09c6ecd39   Dimitris Papastamos   regmap: Add suppo...
306
  	.write = regmap_map_write_file,
31244e396   Mark Brown   regmap: Provide r...
307
308
  	.llseek = default_llseek,
  };
4b020b3f9   Mark Brown   regmap: Provide d...
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  static ssize_t regmap_range_read_file(struct file *file, char __user *user_buf,
  				      size_t count, loff_t *ppos)
  {
  	struct regmap_range_node *range = file->private_data;
  	struct regmap *map = range->map;
  
  	return regmap_read_debugfs(map, range->range_min, range->range_max,
  				   user_buf, count, ppos);
  }
  
  static const struct file_operations regmap_range_fops = {
  	.open = simple_open,
  	.read = regmap_range_read_file,
  	.llseek = default_llseek,
  };
065b4c587   Dimitris Papastamos   regmap: debugfs: ...
324
325
326
327
328
329
330
331
332
333
334
  static ssize_t regmap_reg_ranges_read_file(struct file *file,
  					   char __user *user_buf, size_t count,
  					   loff_t *ppos)
  {
  	struct regmap *map = file->private_data;
  	struct regmap_debugfs_off_cache *c;
  	loff_t p = 0;
  	size_t buf_pos = 0;
  	char *buf;
  	char *entry;
  	int ret;
e34dc4907   Rasmus Villemoes   regmap: debugfs: ...
335
  	unsigned entry_len;
065b4c587   Dimitris Papastamos   regmap: debugfs: ...
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
  
  	if (*ppos < 0 || !count)
  		return -EINVAL;
  
  	buf = kmalloc(count, GFP_KERNEL);
  	if (!buf)
  		return -ENOMEM;
  
  	entry = kmalloc(PAGE_SIZE, GFP_KERNEL);
  	if (!entry) {
  		kfree(buf);
  		return -ENOMEM;
  	}
  
  	/* While we are at it, build the register dump cache
  	 * now so the read() operation on the `registers' file
  	 * can benefit from using the cache.  We do not care
  	 * about the file position information that is contained
  	 * in the cache, just about the actual register blocks */
  	regmap_calc_tot_len(map, buf, count);
  	regmap_debugfs_get_dump_start(map, 0, *ppos, &p);
  
  	/* Reset file pointer as the fixed-format of the `registers'
  	 * file is not compatible with the `range' file */
  	p = 0;
  	mutex_lock(&map->cache_lock);
  	list_for_each_entry(c, &map->debugfs_off_cache, list) {
ca07e9f3c   Rasmus Villemoes   regmap: debugfs: ...
363
364
  		entry_len = snprintf(entry, PAGE_SIZE, "%x-%x
  ",
e34dc4907   Rasmus Villemoes   regmap: debugfs: ...
365
  				     c->base_reg, c->max_reg);
065b4c587   Dimitris Papastamos   regmap: debugfs: ...
366
  		if (p >= *ppos) {
ca07e9f3c   Rasmus Villemoes   regmap: debugfs: ...
367
  			if (buf_pos + entry_len > count)
065b4c587   Dimitris Papastamos   regmap: debugfs: ...
368
  				break;
20991cdb2   Rasmus Villemoes   regmap: debugfs: ...
369
  			memcpy(buf + buf_pos, entry, entry_len);
e34dc4907   Rasmus Villemoes   regmap: debugfs: ...
370
  			buf_pos += entry_len;
065b4c587   Dimitris Papastamos   regmap: debugfs: ...
371
  		}
ca07e9f3c   Rasmus Villemoes   regmap: debugfs: ...
372
  		p += entry_len;
065b4c587   Dimitris Papastamos   regmap: debugfs: ...
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  	}
  	mutex_unlock(&map->cache_lock);
  
  	kfree(entry);
  	ret = buf_pos;
  
  	if (copy_to_user(user_buf, buf, buf_pos)) {
  		ret = -EFAULT;
  		goto out_buf;
  	}
  
  	*ppos += buf_pos;
  out_buf:
  	kfree(buf);
  	return ret;
  }
  
  static const struct file_operations regmap_reg_ranges_fops = {
  	.open = simple_open,
  	.read = regmap_reg_ranges_read_file,
  	.llseek = default_llseek,
  };
8da61f24c   Mark Brown   regmap: debugfs: ...
395
  static int regmap_access_show(struct seq_file *s, void *ignored)
449e38427   Mark Brown   regmap: Provide a...
396
  {
8da61f24c   Mark Brown   regmap: debugfs: ...
397
398
  	struct regmap *map = s->private;
  	int i, reg_len;
449e38427   Mark Brown   regmap: Provide a...
399

9ae3109d1   Mark Brown   regmap: debugfs: ...
400
  	reg_len = regmap_calc_reg_len(map->max_register);
449e38427   Mark Brown   regmap: Provide a...
401

f01ee60ff   Stephen Warren   regmap: implement...
402
  	for (i = 0; i <= map->max_register; i += map->reg_stride) {
449e38427   Mark Brown   regmap: Provide a...
403
404
405
  		/* Ignore registers which are neither readable nor writable */
  		if (!regmap_readable(map, i) && !regmap_writeable(map, i))
  			continue;
8da61f24c   Mark Brown   regmap: debugfs: ...
406
407
408
409
410
411
412
  		/* Format the register */
  		seq_printf(s, "%.*x: %c %c %c %c
  ", reg_len, i,
  			   regmap_readable(map, i) ? 'y' : 'n',
  			   regmap_writeable(map, i) ? 'y' : 'n',
  			   regmap_volatile(map, i) ? 'y' : 'n',
  			   regmap_precious(map, i) ? 'y' : 'n');
449e38427   Mark Brown   regmap: Provide a...
413
  	}
8da61f24c   Mark Brown   regmap: debugfs: ...
414
415
  	return 0;
  }
449e38427   Mark Brown   regmap: Provide a...
416

8da61f24c   Mark Brown   regmap: debugfs: ...
417
418
419
  static int access_open(struct inode *inode, struct file *file)
  {
  	return single_open(file, regmap_access_show, inode->i_private);
449e38427   Mark Brown   regmap: Provide a...
420
421
422
  }
  
  static const struct file_operations regmap_access_fops = {
8da61f24c   Mark Brown   regmap: debugfs: ...
423
424
425
426
  	.open		= access_open,
  	.read		= seq_read,
  	.llseek		= seq_lseek,
  	.release	= single_release,
449e38427   Mark Brown   regmap: Provide a...
427
  };
31244e396   Mark Brown   regmap: Provide r...
428

d3dc5430d   Richard Fitzgerald   regmap: debugfs: ...
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
  static ssize_t regmap_cache_only_write_file(struct file *file,
  					    const char __user *user_buf,
  					    size_t count, loff_t *ppos)
  {
  	struct regmap *map = container_of(file->private_data,
  					  struct regmap, cache_only);
  	ssize_t result;
  	bool was_enabled, require_sync = false;
  	int err;
  
  	map->lock(map->lock_arg);
  
  	was_enabled = map->cache_only;
  
  	result = debugfs_write_file_bool(file, user_buf, count, ppos);
  	if (result < 0) {
  		map->unlock(map->lock_arg);
  		return result;
  	}
  
  	if (map->cache_only && !was_enabled) {
  		dev_warn(map->dev, "debugfs cache_only=Y forced
  ");
  		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
  	} else if (!map->cache_only && was_enabled) {
  		dev_warn(map->dev, "debugfs cache_only=N forced: syncing cache
  ");
  		require_sync = true;
  	}
  
  	map->unlock(map->lock_arg);
  
  	if (require_sync) {
  		err = regcache_sync(map);
  		if (err)
  			dev_err(map->dev, "Failed to sync cache %d
  ", err);
  	}
  
  	return result;
  }
  
  static const struct file_operations regmap_cache_only_fops = {
  	.open = simple_open,
  	.read = debugfs_read_file_bool,
  	.write = regmap_cache_only_write_file,
  };
  
  static ssize_t regmap_cache_bypass_write_file(struct file *file,
  					      const char __user *user_buf,
  					      size_t count, loff_t *ppos)
  {
  	struct regmap *map = container_of(file->private_data,
  					  struct regmap, cache_bypass);
  	ssize_t result;
  	bool was_enabled;
  
  	map->lock(map->lock_arg);
  
  	was_enabled = map->cache_bypass;
  
  	result = debugfs_write_file_bool(file, user_buf, count, ppos);
  	if (result < 0)
  		goto out;
  
  	if (map->cache_bypass && !was_enabled) {
  		dev_warn(map->dev, "debugfs cache_bypass=Y forced
  ");
  		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
  	} else if (!map->cache_bypass && was_enabled) {
  		dev_warn(map->dev, "debugfs cache_bypass=N forced
  ");
  	}
  
  out:
  	map->unlock(map->lock_arg);
  
  	return result;
  }
  
  static const struct file_operations regmap_cache_bypass_fops = {
  	.open = simple_open,
  	.read = debugfs_read_file_bool,
  	.write = regmap_cache_bypass_write_file,
  };
d3c242e1f   Stephen Warren   regmap: allow reg...
514
  void regmap_debugfs_init(struct regmap *map, const char *name)
31244e396   Mark Brown   regmap: Provide r...
515
  {
4b020b3f9   Mark Brown   regmap: Provide d...
516
517
  	struct rb_node *next;
  	struct regmap_range_node *range_node;
2c98e0c1c   Xiubo Li   regmap: debugfs: ...
518
  	const char *devname = "dummy";
4b020b3f9   Mark Brown   regmap: Provide d...
519

a52eaeb18   Tero Kristo   regmap: debugfs: ...
520
521
522
523
524
525
526
527
528
529
530
531
532
  	/* If we don't have the debugfs root yet, postpone init */
  	if (!regmap_debugfs_root) {
  		struct regmap_debugfs_node *node;
  		node = kzalloc(sizeof(*node), GFP_KERNEL);
  		if (!node)
  			return;
  		node->map = map;
  		node->name = name;
  		mutex_lock(&regmap_debugfs_early_lock);
  		list_add(&node->link, &regmap_debugfs_early_list);
  		mutex_unlock(&regmap_debugfs_early_lock);
  		return;
  	}
5166b7c00   Mark Brown   regmap: debugfs: ...
533
  	INIT_LIST_HEAD(&map->debugfs_off_cache);
065b4c587   Dimitris Papastamos   regmap: debugfs: ...
534
  	mutex_init(&map->cache_lock);
5166b7c00   Mark Brown   regmap: debugfs: ...
535

2c98e0c1c   Xiubo Li   regmap: debugfs: ...
536
537
  	if (map->dev)
  		devname = dev_name(map->dev);
d3c242e1f   Stephen Warren   regmap: allow reg...
538
539
  	if (name) {
  		map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
2c98e0c1c   Xiubo Li   regmap: debugfs: ...
540
  					      devname, name);
d3c242e1f   Stephen Warren   regmap: allow reg...
541
542
  		name = map->debugfs_name;
  	} else {
2c98e0c1c   Xiubo Li   regmap: debugfs: ...
543
  		name = devname;
d3c242e1f   Stephen Warren   regmap: allow reg...
544
545
546
  	}
  
  	map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
31244e396   Mark Brown   regmap: Provide r...
547
548
549
550
551
  	if (!map->debugfs) {
  		dev_warn(map->dev, "Failed to create debugfs directory
  ");
  		return;
  	}
f0c2319f9   Dimitris Papastamos   regmap: Expose th...
552
553
  	debugfs_create_file("name", 0400, map->debugfs,
  			    map, &regmap_name_fops);
065b4c587   Dimitris Papastamos   regmap: debugfs: ...
554
555
  	debugfs_create_file("range", 0400, map->debugfs,
  			    map, &regmap_reg_ranges_fops);
676970da5   Pawel Moll   regmap: debugfs: ...
556
  	if (map->max_register || regmap_readable(map, 0)) {
ffff7a12a   Markus Pargmann   regmap: Fix debug...
557
  		umode_t registers_mode;
1635e8888   Axel Lin   regmap: debugfs: ...
558
559
560
561
562
  #if defined(REGMAP_ALLOW_WRITE_DEBUGFS)
  		registers_mode = 0600;
  #else
  		registers_mode = 0400;
  #endif
ffff7a12a   Markus Pargmann   regmap: Fix debug...
563
564
  
  		debugfs_create_file("registers", registers_mode, map->debugfs,
31244e396   Mark Brown   regmap: Provide r...
565
  				    map, &regmap_map_fops);
449e38427   Mark Brown   regmap: Provide a...
566
567
568
  		debugfs_create_file("access", 0400, map->debugfs,
  				    map, &regmap_access_fops);
  	}
028a01e60   Mark Brown   regmap: Add debug...
569
570
  
  	if (map->cache_type) {
d3dc5430d   Richard Fitzgerald   regmap: debugfs: ...
571
572
  		debugfs_create_file("cache_only", 0600, map->debugfs,
  				    &map->cache_only, &regmap_cache_only_fops);
028a01e60   Mark Brown   regmap: Add debug...
573
574
  		debugfs_create_bool("cache_dirty", 0400, map->debugfs,
  				    &map->cache_dirty);
d3dc5430d   Richard Fitzgerald   regmap: debugfs: ...
575
576
577
  		debugfs_create_file("cache_bypass", 0600, map->debugfs,
  				    &map->cache_bypass,
  				    &regmap_cache_bypass_fops);
028a01e60   Mark Brown   regmap: Add debug...
578
  	}
4b020b3f9   Mark Brown   regmap: Provide d...
579
580
581
582
583
584
585
586
587
588
589
590
  
  	next = rb_first(&map->range_tree);
  	while (next) {
  		range_node = rb_entry(next, struct regmap_range_node, node);
  
  		if (range_node->name)
  			debugfs_create_file(range_node->name, 0400,
  					    map->debugfs, range_node,
  					    &regmap_range_fops);
  
  		next = rb_next(&range_node->node);
  	}
5e0cbe787   Lars-Peter Clausen   regmap: Fix regca...
591
592
593
  
  	if (map->cache_ops && map->cache_ops->debugfs_init)
  		map->cache_ops->debugfs_init(map);
31244e396   Mark Brown   regmap: Provide r...
594
595
596
597
  }
  
  void regmap_debugfs_exit(struct regmap *map)
  {
a52eaeb18   Tero Kristo   regmap: debugfs: ...
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
  	if (map->debugfs) {
  		debugfs_remove_recursive(map->debugfs);
  		mutex_lock(&map->cache_lock);
  		regmap_debugfs_free_dump_cache(map);
  		mutex_unlock(&map->cache_lock);
  		kfree(map->debugfs_name);
  	} else {
  		struct regmap_debugfs_node *node, *tmp;
  
  		mutex_lock(&regmap_debugfs_early_lock);
  		list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list,
  					 link) {
  			if (node->map == map) {
  				list_del(&node->link);
  				kfree(node);
  			}
  		}
  		mutex_unlock(&regmap_debugfs_early_lock);
  	}
31244e396   Mark Brown   regmap: Provide r...
617
618
619
620
  }
  
  void regmap_debugfs_initcall(void)
  {
a52eaeb18   Tero Kristo   regmap: debugfs: ...
621
  	struct regmap_debugfs_node *node, *tmp;
31244e396   Mark Brown   regmap: Provide r...
622
623
624
625
626
627
  	regmap_debugfs_root = debugfs_create_dir("regmap", NULL);
  	if (!regmap_debugfs_root) {
  		pr_warn("regmap: Failed to create debugfs root
  ");
  		return;
  	}
a52eaeb18   Tero Kristo   regmap: debugfs: ...
628
629
630
631
632
633
634
635
  
  	mutex_lock(&regmap_debugfs_early_lock);
  	list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list, link) {
  		regmap_debugfs_init(node->map, node->name);
  		list_del(&node->link);
  		kfree(node);
  	}
  	mutex_unlock(&regmap_debugfs_early_lock);
31244e396   Mark Brown   regmap: Provide r...
636
  }