2011-07-21 01:56:53 +04:00
/*
* 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>
# include <linux/mutex.h>
# include <linux/debugfs.h>
# include <linux/uaccess.h>
2012-01-22 20:23:42 +04:00
# include <linux/device.h>
2011-07-21 01:56:53 +04:00
# include "internal.h"
static struct dentry * regmap_debugfs_root ;
2011-08-10 12:15:31 +04:00
/* Calculate the length of a fixed format */
static size_t regmap_calc_reg_len ( int max_val , char * buf , size_t buf_size )
{
snprintf ( buf , buf_size , " %x " , max_val ) ;
return strlen ( buf ) ;
}
2012-02-22 18:20:09 +04:00
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 \n " , 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 = {
2012-04-06 01:25:11 +04:00
. open = simple_open ,
2012-02-22 18:20:09 +04:00
. read = regmap_name_read_file ,
. llseek = default_llseek ,
} ;
2013-01-08 17:35:58 +04:00
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 ) ;
}
}
2012-12-09 12:20:10 +04:00
/*
* 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 )
{
2012-12-10 20:24:29 +04:00
struct regmap_debugfs_off_cache * c = NULL ;
loff_t p = 0 ;
unsigned int i , ret ;
2013-02-08 16:47:14 +04:00
unsigned int fpos_offset ;
unsigned int reg_offset ;
2012-12-10 20:24:29 +04:00
/*
* If we don ' t have a cache build one so we don ' t have to do a
* linear scan each time .
*/
if ( list_empty ( & map - > debugfs_off_cache ) ) {
for ( i = base ; i < = map - > max_register ; i + = map - > reg_stride ) {
/* Skip unprinted registers, closing off cache entry */
if ( ! regmap_readable ( map , i ) | |
regmap_precious ( map , i ) ) {
if ( c ) {
c - > max = p - 1 ;
2013-02-08 16:47:14 +04:00
fpos_offset = c - > max - c - > min ;
reg_offset = fpos_offset / map - > debugfs_tot_len ;
c - > max_reg = c - > base_reg + reg_offset ;
2012-12-10 20:24:29 +04:00
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 ) ;
2013-01-08 17:35:58 +04:00
if ( ! c ) {
regmap_debugfs_free_dump_cache ( map ) ;
return base ;
}
2012-12-10 20:24:29 +04:00
c - > min = p ;
c - > base_reg = i ;
}
p + = map - > debugfs_tot_len ;
}
}
2012-12-09 12:20:10 +04:00
2013-01-08 22:47:52 +04:00
/* Close the last entry off if we didn't scan beyond it */
if ( c ) {
c - > max = p - 1 ;
2013-02-08 16:47:14 +04:00
fpos_offset = c - > max - c - > min ;
reg_offset = fpos_offset / map - > debugfs_tot_len ;
c - > max_reg = c - > base_reg + reg_offset ;
2013-01-08 22:47:52 +04:00
list_add_tail ( & c - > list ,
& map - > debugfs_off_cache ) ;
}
2013-01-08 17:44:50 +04:00
/*
* 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 .
*/
2013-01-26 15:45:35 +04:00
WARN_ON ( list_empty ( & map - > debugfs_off_cache ) ) ;
ret = base ;
2013-01-08 17:44:50 +04:00
2013-02-08 16:47:20 +04:00
/* Find the relevant block:offset */
2012-12-10 20:24:29 +04:00
list_for_each_entry ( c , & map - > debugfs_off_cache , list ) {
2013-01-09 00:40:19 +04:00
if ( from > = c - > min & & from < = c - > max ) {
2013-02-08 16:47:20 +04:00
fpos_offset = from - c - > min ;
reg_offset = fpos_offset / map - > debugfs_tot_len ;
* pos = c - > min + ( reg_offset * map - > debugfs_tot_len ) ;
return c - > base_reg + reg_offset ;
2012-12-09 12:20:10 +04:00
}
2013-02-08 16:47:20 +04:00
* pos = c - > max ;
ret = c - > max_reg ;
2012-12-09 12:20:10 +04:00
}
2012-12-10 20:24:29 +04:00
return ret ;
2012-12-09 12:20:10 +04:00
}
2013-02-11 14:50:59 +04:00
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 ) {
map - > debugfs_reg_len = regmap_calc_reg_len ( map - > max_register ,
buf , count ) ;
map - > debugfs_val_len = 2 * map - > format . val_bytes ;
map - > debugfs_tot_len = map - > debugfs_reg_len +
map - > debugfs_val_len + 3 ; /* : \n */
}
}
2012-10-03 15:45:37 +04:00
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 )
2011-07-21 01:56:53 +04:00
{
size_t buf_pos = 0 ;
2012-12-09 12:20:10 +04:00
loff_t p = * ppos ;
2011-07-21 01:56:53 +04:00
ssize_t ret ;
int i ;
char * buf ;
2012-12-09 12:20:10 +04:00
unsigned int val , start_reg ;
2011-07-21 01:56:53 +04:00
if ( * ppos < 0 | | ! count )
return - EINVAL ;
buf = kmalloc ( count , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
2013-02-11 14:50:59 +04:00
regmap_calc_tot_len ( map , buf , count ) ;
2011-07-21 01:56:53 +04:00
2012-12-09 12:20:10 +04:00
/* 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 ) {
2011-08-10 12:14:41 +04:00
if ( ! regmap_readable ( map , i ) )
2011-07-21 01:56:53 +04:00
continue ;
2011-08-10 12:14:41 +04:00
if ( regmap_precious ( map , i ) )
2011-08-08 10:41:46 +04:00
continue ;
2011-07-21 01:56:53 +04:00
/* If we're in the region the user is trying to read */
if ( p > = * ppos ) {
/* ...but not beyond it */
2013-02-07 14:51:56 +04:00
if ( buf_pos + map - > debugfs_tot_len > count )
2011-07-21 01:56:53 +04:00
break ;
/* Format the register */
snprintf ( buf + buf_pos , count - buf_pos , " %.*x: " ,
2012-12-06 08:29:05 +04:00
map - > debugfs_reg_len , i - from ) ;
buf_pos + = map - > debugfs_reg_len + 2 ;
2011-07-21 01:56:53 +04:00
/* 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 ,
2012-12-06 08:29:05 +04:00
" %.*x " , map - > debugfs_val_len , val ) ;
2011-07-21 01:56:53 +04:00
else
2012-12-06 08:29:05 +04:00
memset ( buf + buf_pos , ' X ' ,
map - > debugfs_val_len ) ;
2011-07-21 01:56:53 +04:00
buf_pos + = 2 * map - > format . val_bytes ;
buf [ buf_pos + + ] = ' \n ' ;
}
2012-12-06 08:29:05 +04:00
p + = map - > debugfs_tot_len ;
2011-07-21 01:56:53 +04:00
}
ret = buf_pos ;
if ( copy_to_user ( user_buf , buf , buf_pos ) ) {
ret = - EFAULT ;
goto out ;
}
* ppos + = buf_pos ;
out :
kfree ( buf ) ;
return ret ;
}
2012-10-03 15:45:37 +04:00
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 ) ;
}
2012-02-22 16:43:50 +04:00
# 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 ;
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 + + ;
if ( strict_strtoul ( start , 16 , & value ) )
return - EINVAL ;
/* Userspace has been fiddling around behind the kernel's back */
2013-01-21 10:47:39 +04:00
add_taint ( TAINT_USER , LOCKDEP_NOW_UNRELIABLE ) ;
2012-02-22 16:43:50 +04:00
regmap_write ( map , reg , value ) ;
return buf_size ;
}
# else
# define regmap_map_write_file NULL
# endif
2011-07-21 01:56:53 +04:00
static const struct file_operations regmap_map_fops = {
2012-04-06 01:25:11 +04:00
. open = simple_open ,
2011-07-21 01:56:53 +04:00
. read = regmap_map_read_file ,
2012-02-22 16:43:50 +04:00
. write = regmap_map_write_file ,
2011-07-21 01:56:53 +04:00
. llseek = default_llseek ,
} ;
2012-10-03 16:13:16 +04:00
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 ,
} ;
2011-08-10 12:28:04 +04:00
static ssize_t regmap_access_read_file ( struct file * file ,
char __user * user_buf , size_t count ,
loff_t * ppos )
{
int reg_len , tot_len ;
size_t buf_pos = 0 ;
loff_t p = 0 ;
ssize_t ret ;
int i ;
struct regmap * map = file - > private_data ;
char * buf ;
if ( * ppos < 0 | | ! count )
return - EINVAL ;
buf = kmalloc ( count , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
/* Calculate the length of a fixed format */
reg_len = regmap_calc_reg_len ( map - > max_register , buf , count ) ;
tot_len = reg_len + 10 ; /* ': R W V P\n' */
2012-04-09 23:40:24 +04:00
for ( i = 0 ; i < = map - > max_register ; i + = map - > reg_stride ) {
2011-08-10 12:28:04 +04:00
/* Ignore registers which are neither readable nor writable */
if ( ! regmap_readable ( map , i ) & & ! regmap_writeable ( map , i ) )
continue ;
/* If we're in the region the user is trying to read */
if ( p > = * ppos ) {
/* ...but not beyond it */
if ( buf_pos > = count - 1 - tot_len )
break ;
/* Format the register */
snprintf ( buf + buf_pos , count - buf_pos ,
" %.*x: %c %c %c %c \n " ,
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 ' ) ;
buf_pos + = tot_len ;
}
p + = tot_len ;
}
ret = buf_pos ;
if ( copy_to_user ( user_buf , buf , buf_pos ) ) {
ret = - EFAULT ;
goto out ;
}
* ppos + = buf_pos ;
out :
kfree ( buf ) ;
return ret ;
}
static const struct file_operations regmap_access_fops = {
2012-04-06 01:25:11 +04:00
. open = simple_open ,
2011-08-10 12:28:04 +04:00
. read = regmap_access_read_file ,
. llseek = default_llseek ,
} ;
2011-07-21 01:56:53 +04:00
2012-04-05 01:48:29 +04:00
void regmap_debugfs_init ( struct regmap * map , const char * name )
2011-07-21 01:56:53 +04:00
{
2012-10-03 16:13:16 +04:00
struct rb_node * next ;
struct regmap_range_node * range_node ;
2012-12-10 20:24:29 +04:00
INIT_LIST_HEAD ( & map - > debugfs_off_cache ) ;
2012-04-05 01:48:29 +04:00
if ( name ) {
map - > debugfs_name = kasprintf ( GFP_KERNEL , " %s-%s " ,
dev_name ( map - > dev ) , name ) ;
name = map - > debugfs_name ;
} else {
name = dev_name ( map - > dev ) ;
}
map - > debugfs = debugfs_create_dir ( name , regmap_debugfs_root ) ;
2011-07-21 01:56:53 +04:00
if ( ! map - > debugfs ) {
dev_warn ( map - > dev , " Failed to create debugfs directory \n " ) ;
return ;
}
2012-02-22 18:20:09 +04:00
debugfs_create_file ( " name " , 0400 , map - > debugfs ,
map , & regmap_name_fops ) ;
2011-08-10 12:28:04 +04:00
if ( map - > max_register ) {
2011-07-21 01:56:53 +04:00
debugfs_create_file ( " registers " , 0400 , map - > debugfs ,
map , & regmap_map_fops ) ;
2011-08-10 12:28:04 +04:00
debugfs_create_file ( " access " , 0400 , map - > debugfs ,
map , & regmap_access_fops ) ;
}
2012-02-06 22:02:06 +04:00
if ( map - > cache_type ) {
debugfs_create_bool ( " cache_only " , 0400 , map - > debugfs ,
& map - > cache_only ) ;
debugfs_create_bool ( " cache_dirty " , 0400 , map - > debugfs ,
& map - > cache_dirty ) ;
debugfs_create_bool ( " cache_bypass " , 0400 , map - > debugfs ,
& map - > cache_bypass ) ;
}
2012-10-03 16:13:16 +04:00
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 ) ;
}
2011-07-21 01:56:53 +04:00
}
void regmap_debugfs_exit ( struct regmap * map )
{
debugfs_remove_recursive ( map - > debugfs ) ;
2013-01-08 17:35:58 +04:00
regmap_debugfs_free_dump_cache ( map ) ;
2012-04-05 01:48:29 +04:00
kfree ( map - > debugfs_name ) ;
2011-07-21 01:56:53 +04:00
}
void regmap_debugfs_initcall ( void )
{
regmap_debugfs_root = debugfs_create_dir ( " regmap " , NULL ) ;
if ( ! regmap_debugfs_root ) {
pr_warn ( " regmap: Failed to create debugfs root \n " ) ;
return ;
}
}