Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
bcf86687
Commit
bcf86687
authored
Dec 11, 2012
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'regmap/topic/debugfs' into regmap-next
parents
d3816c1a
5166b7c0
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
260 additions
and
64 deletions
+260
-64
drivers/base/regmap/internal.h
drivers/base/regmap/internal.h
+16
-0
drivers/base/regmap/regmap-debugfs.c
drivers/base/regmap/regmap-debugfs.c
+132
-16
drivers/base/regmap/regmap.c
drivers/base/regmap/regmap.c
+107
-47
include/linux/regmap.h
include/linux/regmap.h
+5
-1
No files found.
drivers/base/regmap/internal.h
View file @
bcf86687
...
...
@@ -15,10 +15,18 @@
#include <linux/regmap.h>
#include <linux/fs.h>
#include <linux/list.h>
struct
regmap
;
struct
regcache_ops
;
struct
regmap_debugfs_off_cache
{
struct
list_head
list
;
off_t
min
;
off_t
max
;
unsigned
int
base_reg
;
};
struct
regmap_format
{
size_t
buf_size
;
size_t
reg_bytes
;
...
...
@@ -50,6 +58,12 @@ struct regmap {
#ifdef CONFIG_DEBUG_FS
struct
dentry
*
debugfs
;
const
char
*
debugfs_name
;
unsigned
int
debugfs_reg_len
;
unsigned
int
debugfs_val_len
;
unsigned
int
debugfs_tot_len
;
struct
list_head
debugfs_off_cache
;
#endif
unsigned
int
max_register
;
...
...
@@ -120,6 +134,8 @@ int _regmap_write(struct regmap *map, unsigned int reg,
struct
regmap_range_node
{
struct
rb_node
node
;
const
char
*
name
;
struct
regmap
*
map
;
unsigned
int
range_min
;
unsigned
int
range_max
;
...
...
drivers/base/regmap/regmap-debugfs.c
View file @
bcf86687
...
...
@@ -56,17 +56,74 @@ static const struct file_operations regmap_name_fops = {
.
llseek
=
default_llseek
,
};
static
ssize_t
regmap_map_read_file
(
struct
file
*
file
,
char
__user
*
user_buf
,
size_t
count
,
loff_t
*
ppos
)
/*
* 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
)
{
int
reg_len
,
val_len
,
tot_len
;
size_t
buf_pos
=
0
;
struct
regmap_debugfs_off_cache
*
c
=
NULL
;
loff_t
p
=
0
;
unsigned
int
i
,
ret
;
/*
* 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
;
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
);
if
(
!
c
)
break
;
c
->
min
=
p
;
c
->
base_reg
=
i
;
}
p
+=
map
->
debugfs_tot_len
;
}
}
/* Find the relevant block */
list_for_each_entry
(
c
,
&
map
->
debugfs_off_cache
,
list
)
{
if
(
*
pos
>=
c
->
min
&&
*
pos
<=
c
->
max
)
{
*
pos
=
c
->
min
;
return
c
->
base_reg
;
}
ret
=
c
->
max
;
}
return
ret
;
}
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
)
{
size_t
buf_pos
=
0
;
loff_t
p
=
*
ppos
;
ssize_t
ret
;
int
i
;
struct
regmap
*
map
=
file
->
private_data
;
char
*
buf
;
unsigned
int
val
;
unsigned
int
val
,
start_reg
;
if
(
*
ppos
<
0
||
!
count
)
return
-
EINVAL
;
...
...
@@ -76,11 +133,18 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
return
-
ENOMEM
;
/* Calculate the length of a fixed format */
reg_len
=
regmap_calc_reg_len
(
map
->
max_register
,
buf
,
count
);
val_len
=
2
*
map
->
format
.
val_bytes
;
tot_len
=
reg_len
+
val_len
+
3
;
/* : \n */
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 */
}
for
(
i
=
0
;
i
<=
map
->
max_register
;
i
+=
map
->
reg_stride
)
{
/* 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
)
{
if
(
!
regmap_readable
(
map
,
i
))
continue
;
...
...
@@ -90,26 +154,27 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
/* 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
)
if
(
buf_pos
+
1
+
map
->
debugfs_tot_len
>=
count
)
break
;
/* Format the register */
snprintf
(
buf
+
buf_pos
,
count
-
buf_pos
,
"%.*x: "
,
reg_len
,
i
);
buf_pos
+=
reg_len
+
2
;
map
->
debugfs_reg_len
,
i
-
from
);
buf_pos
+=
map
->
debugfs_
reg_len
+
2
;
/* 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
,
"%.*x"
,
val_len
,
val
);
"%.*x"
,
map
->
debugfs_
val_len
,
val
);
else
memset
(
buf
+
buf_pos
,
'X'
,
val_len
);
memset
(
buf
+
buf_pos
,
'X'
,
map
->
debugfs_val_len
);
buf_pos
+=
2
*
map
->
format
.
val_bytes
;
buf
[
buf_pos
++
]
=
'\n'
;
}
p
+=
tot_len
;
p
+=
map
->
debugfs_
tot_len
;
}
ret
=
buf_pos
;
...
...
@@ -126,6 +191,15 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
return
ret
;
}
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
);
}
#undef REGMAP_ALLOW_WRITE_DEBUGFS
#ifdef REGMAP_ALLOW_WRITE_DEBUGFS
/*
...
...
@@ -174,6 +248,22 @@ static const struct file_operations regmap_map_fops = {
.
llseek
=
default_llseek
,
};
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
,
};
static
ssize_t
regmap_access_read_file
(
struct
file
*
file
,
char
__user
*
user_buf
,
size_t
count
,
loff_t
*
ppos
)
...
...
@@ -244,6 +334,11 @@ static const struct file_operations regmap_access_fops = {
void
regmap_debugfs_init
(
struct
regmap
*
map
,
const
char
*
name
)
{
struct
rb_node
*
next
;
struct
regmap_range_node
*
range_node
;
INIT_LIST_HEAD
(
&
map
->
debugfs_off_cache
);
if
(
name
)
{
map
->
debugfs_name
=
kasprintf
(
GFP_KERNEL
,
"%s-%s"
,
dev_name
(
map
->
dev
),
name
);
...
...
@@ -276,11 +371,32 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
debugfs_create_bool
(
"cache_bypass"
,
0400
,
map
->
debugfs
,
&
map
->
cache_bypass
);
}
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
);
}
}
void
regmap_debugfs_exit
(
struct
regmap
*
map
)
{
struct
regmap_debugfs_off_cache
*
c
;
debugfs_remove_recursive
(
map
->
debugfs
);
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
);
}
kfree
(
map
->
debugfs_name
);
}
...
...
drivers/base/regmap/regmap.c
View file @
bcf86687
...
...
@@ -519,20 +519,38 @@ struct regmap *regmap_init(struct device *dev,
}
map
->
range_tree
=
RB_ROOT
;
for
(
i
=
0
;
i
<
config
->
n_ranges
;
i
++
)
{
for
(
i
=
0
;
i
<
config
->
n
um
_ranges
;
i
++
)
{
const
struct
regmap_range_cfg
*
range_cfg
=
&
config
->
ranges
[
i
];
struct
regmap_range_node
*
new
;
/* Sanity check */
if
(
range_cfg
->
range_max
<
range_cfg
->
range_min
||
range_cfg
->
range_max
>
map
->
max_register
||
range_cfg
->
selector_reg
>
map
->
max_register
||
range_cfg
->
window_len
==
0
)
if
(
range_cfg
->
range_max
<
range_cfg
->
range_min
)
{
dev_err
(
map
->
dev
,
"Invalid range %d: %d < %d
\n
"
,
i
,
range_cfg
->
range_max
,
range_cfg
->
range_min
);
goto
err_range
;
}
if
(
range_cfg
->
range_max
>
map
->
max_register
)
{
dev_err
(
map
->
dev
,
"Invalid range %d: %d > %d
\n
"
,
i
,
range_cfg
->
range_max
,
map
->
max_register
);
goto
err_range
;
}
if
(
range_cfg
->
selector_reg
>
map
->
max_register
)
{
dev_err
(
map
->
dev
,
"Invalid range %d: selector out of map
\n
"
,
i
);
goto
err_range
;
}
if
(
range_cfg
->
window_len
==
0
)
{
dev_err
(
map
->
dev
,
"Invalid range %d: window_len 0
\n
"
,
i
);
goto
err_range
;
}
/* Make sure, that this register range has no selector
or data window within its boundary */
for
(
j
=
0
;
j
<
config
->
n_ranges
;
j
++
)
{
for
(
j
=
0
;
j
<
config
->
n
um
_ranges
;
j
++
)
{
unsigned
sel_reg
=
config
->
ranges
[
j
].
selector_reg
;
unsigned
win_min
=
config
->
ranges
[
j
].
window_start
;
unsigned
win_max
=
win_min
+
...
...
@@ -540,11 +558,17 @@ struct regmap *regmap_init(struct device *dev,
if
(
range_cfg
->
range_min
<=
sel_reg
&&
sel_reg
<=
range_cfg
->
range_max
)
{
dev_err
(
map
->
dev
,
"Range %d: selector for %d in window
\n
"
,
i
,
j
);
goto
err_range
;
}
if
(
!
(
win_max
<
range_cfg
->
range_min
||
win_min
>
range_cfg
->
range_max
))
{
dev_err
(
map
->
dev
,
"Range %d: window for %d in window
\n
"
,
i
,
j
);
goto
err_range
;
}
}
...
...
@@ -555,6 +579,8 @@ struct regmap *regmap_init(struct device *dev,
goto
err_range
;
}
new
->
map
=
map
;
new
->
name
=
range_cfg
->
name
;
new
->
range_min
=
range_cfg
->
range_min
;
new
->
range_max
=
range_cfg
->
range_max
;
new
->
selector_reg
=
range_cfg
->
selector_reg
;
...
...
@@ -564,6 +590,7 @@ struct regmap *regmap_init(struct device *dev,
new
->
window_len
=
range_cfg
->
window_len
;
if
(
_regmap_range_add
(
map
,
new
)
==
false
)
{
dev_err
(
map
->
dev
,
"Failed to add range %d
\n
"
,
i
);
kfree
(
new
);
goto
err_range
;
}
...
...
@@ -579,7 +606,7 @@ struct regmap *regmap_init(struct device *dev,
}
ret
=
regcache_init
(
map
,
config
);
if
(
ret
<
0
)
if
(
ret
!=
0
)
goto
err_range
;
regmap_debugfs_init
(
map
,
config
->
name
);
...
...
@@ -738,59 +765,57 @@ struct regmap *dev_get_regmap(struct device *dev, const char *name)
EXPORT_SYMBOL_GPL
(
dev_get_regmap
);
static
int
_regmap_select_page
(
struct
regmap
*
map
,
unsigned
int
*
reg
,
struct
regmap_range_node
*
range
,
unsigned
int
val_num
)
{
struct
regmap_range_node
*
range
;
void
*
orig_work_buf
;
unsigned
int
win_offset
;
unsigned
int
win_page
;
bool
page_chg
;
int
ret
;
range
=
_regmap_range_lookup
(
map
,
*
reg
);
if
(
range
)
{
win_offset
=
(
*
reg
-
range
->
range_min
)
%
range
->
window_len
;
win_page
=
(
*
reg
-
range
->
range_min
)
/
range
->
window_len
;
if
(
val_num
>
1
)
{
/* Bulk write shouldn't cross range boundary */
if
(
*
reg
+
val_num
-
1
>
range
->
range_max
)
return
-
EINVAL
;
win_offset
=
(
*
reg
-
range
->
range_min
)
%
range
->
window_len
;
win_page
=
(
*
reg
-
range
->
range_min
)
/
range
->
window_len
;
/* ... or single page boundary */
if
(
val_num
>
range
->
window_len
-
win_offset
)
return
-
EINVAL
;
}
if
(
val_num
>
1
)
{
/* Bulk write shouldn't cross range boundary */
if
(
*
reg
+
val_num
-
1
>
range
->
range_max
)
return
-
EINVAL
;
/* It is possible to have selector register inside data window.
In that case, selector register is located on every page and
it needs no page switching, when accessed alone. */
if
(
val_num
>
1
||
range
->
window_start
+
win_offset
!=
range
->
selector_reg
)
{
/* Use separate work_buf during page switching */
orig_work_buf
=
map
->
work_buf
;
map
->
work_buf
=
map
->
selector_work_buf
;
/* ... or single page boundary */
if
(
val_num
>
range
->
window_len
-
win_offset
)
return
-
EINVAL
;
}
ret
=
_regmap_update_bits
(
map
,
range
->
selector_reg
,
range
->
selector_mask
,
win_page
<<
range
->
selector_shift
,
&
page_chg
);
/* It is possible to have selector register inside data window.
In that case, selector register is located on every page and
it needs no page switching, when accessed alone. */
if
(
val_num
>
1
||
range
->
window_start
+
win_offset
!=
range
->
selector_reg
)
{
/* Use separate work_buf during page switching */
orig_work_buf
=
map
->
work_buf
;
map
->
work_buf
=
map
->
selector_work_buf
;
map
->
work_buf
=
orig_work_buf
;
ret
=
_regmap_update_bits
(
map
,
range
->
selector_reg
,
range
->
selector_mask
,
win_page
<<
range
->
selector_shift
,
&
page_chg
);
if
(
ret
<
0
)
return
ret
;
}
map
->
work_buf
=
orig_work_buf
;
*
reg
=
range
->
window_start
+
win_offset
;
if
(
ret
!=
0
)
return
ret
;
}
*
reg
=
range
->
window_start
+
win_offset
;
return
0
;
}
static
int
_regmap_raw_write
(
struct
regmap
*
map
,
unsigned
int
reg
,
const
void
*
val
,
size_t
val_len
)
{
struct
regmap_range_node
*
range
;
u8
*
u8
=
map
->
work_buf
;
void
*
buf
;
int
ret
=
-
ENOTSUPP
;
...
...
@@ -825,9 +850,35 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
}
}
ret
=
_regmap_select_page
(
map
,
&
reg
,
val_len
/
map
->
format
.
val_bytes
);
if
(
ret
<
0
)
return
ret
;
range
=
_regmap_range_lookup
(
map
,
reg
);
if
(
range
)
{
int
val_num
=
val_len
/
map
->
format
.
val_bytes
;
int
win_offset
=
(
reg
-
range
->
range_min
)
%
range
->
window_len
;
int
win_residue
=
range
->
window_len
-
win_offset
;
/* If the write goes beyond the end of the window split it */
while
(
val_num
>
win_residue
)
{
dev_dbg
(
map
->
dev
,
"Writing window %d/%zu
\n
"
,
win_residue
,
val_len
/
map
->
format
.
val_bytes
);
ret
=
_regmap_raw_write
(
map
,
reg
,
val
,
win_residue
*
map
->
format
.
val_bytes
);
if
(
ret
!=
0
)
return
ret
;
reg
+=
win_residue
;
val_num
-=
win_residue
;
val
+=
win_residue
*
map
->
format
.
val_bytes
;
val_len
-=
win_residue
*
map
->
format
.
val_bytes
;
win_offset
=
(
reg
-
range
->
range_min
)
%
range
->
window_len
;
win_residue
=
range
->
window_len
-
win_offset
;
}
ret
=
_regmap_select_page
(
map
,
&
reg
,
range
,
val_num
);
if
(
ret
!=
0
)
return
ret
;
}
map
->
format
.
format_reg
(
map
->
work_buf
,
reg
,
map
->
reg_shift
);
...
...
@@ -876,6 +927,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
int
_regmap_write
(
struct
regmap
*
map
,
unsigned
int
reg
,
unsigned
int
val
)
{
struct
regmap_range_node
*
range
;
int
ret
;
BUG_ON
(
!
map
->
format
.
format_write
&&
!
map
->
format
.
format_val
);
...
...
@@ -897,9 +949,12 @@ int _regmap_write(struct regmap *map, unsigned int reg,
trace_regmap_reg_write
(
map
->
dev
,
reg
,
val
);
if
(
map
->
format
.
format_write
)
{
ret
=
_regmap_select_page
(
map
,
&
reg
,
1
);
if
(
ret
<
0
)
return
ret
;
range
=
_regmap_range_lookup
(
map
,
reg
);
if
(
range
)
{
ret
=
_regmap_select_page
(
map
,
&
reg
,
range
,
1
);
if
(
ret
!=
0
)
return
ret
;
}
map
->
format
.
format_write
(
map
,
reg
,
val
);
...
...
@@ -1055,12 +1110,17 @@ EXPORT_SYMBOL_GPL(regmap_bulk_write);
static
int
_regmap_raw_read
(
struct
regmap
*
map
,
unsigned
int
reg
,
void
*
val
,
unsigned
int
val_len
)
{
struct
regmap_range_node
*
range
;
u8
*
u8
=
map
->
work_buf
;
int
ret
;
ret
=
_regmap_select_page
(
map
,
&
reg
,
val_len
/
map
->
format
.
val_bytes
);
if
(
ret
<
0
)
return
ret
;
range
=
_regmap_range_lookup
(
map
,
reg
);
if
(
range
)
{
ret
=
_regmap_select_page
(
map
,
&
reg
,
range
,
val_len
/
map
->
format
.
val_bytes
);
if
(
ret
!=
0
)
return
ret
;
}
map
->
format
.
format_reg
(
map
->
work_buf
,
reg
,
map
->
reg_shift
);
...
...
include/linux/regmap.h
View file @
bcf86687
...
...
@@ -133,7 +133,7 @@ struct regmap_config {
enum
regmap_endian
val_format_endian
;
const
struct
regmap_range_cfg
*
ranges
;
unsigned
int
n_ranges
;
unsigned
int
n
um
_ranges
;
};
/**
...
...
@@ -142,6 +142,8 @@ struct regmap_config {
* 1. page selector register update;
* 2. access through data window registers.
*
* @name: Descriptive name for diagnostics
*
* @range_min: Address of the lowest register address in virtual range.
* @range_max: Address of the highest register in virtual range.
*
...
...
@@ -153,6 +155,8 @@ struct regmap_config {
* @window_len: Number of registers in data window.
*/
struct
regmap_range_cfg
{
const
char
*
name
;
/* Registers of virtual address range */
unsigned
int
range_min
;
unsigned
int
range_max
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment