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
66fb181d
Commit
66fb181d
authored
Jan 29, 2019
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'regmap/topic/irq' into regmap-next
parents
31172d10
a2d21848
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
126 additions
and
4 deletions
+126
-4
drivers/base/regmap/regmap-irq.c
drivers/base/regmap/regmap-irq.c
+95
-4
include/linux/regmap.h
include/linux/regmap.h
+31
-0
No files found.
drivers/base/regmap/regmap-irq.c
View file @
66fb181d
...
@@ -35,6 +35,7 @@ struct regmap_irq_chip_data {
...
@@ -35,6 +35,7 @@ struct regmap_irq_chip_data {
int
wake_count
;
int
wake_count
;
void
*
status_reg_buf
;
void
*
status_reg_buf
;
unsigned
int
*
main_status_buf
;
unsigned
int
*
status_buf
;
unsigned
int
*
status_buf
;
unsigned
int
*
mask_buf
;
unsigned
int
*
mask_buf
;
unsigned
int
*
mask_buf_def
;
unsigned
int
*
mask_buf_def
;
...
@@ -329,6 +330,33 @@ static const struct irq_chip regmap_irq_chip = {
...
@@ -329,6 +330,33 @@ static const struct irq_chip regmap_irq_chip = {
.
irq_set_wake
=
regmap_irq_set_wake
,
.
irq_set_wake
=
regmap_irq_set_wake
,
};
};
static
inline
int
read_sub_irq_data
(
struct
regmap_irq_chip_data
*
data
,
unsigned
int
b
)
{
const
struct
regmap_irq_chip
*
chip
=
data
->
chip
;
struct
regmap
*
map
=
data
->
map
;
struct
regmap_irq_sub_irq_map
*
subreg
;
int
i
,
ret
=
0
;
if
(
!
chip
->
sub_reg_offsets
)
{
/* Assume linear mapping */
ret
=
regmap_read
(
map
,
chip
->
status_base
+
(
b
*
map
->
reg_stride
*
data
->
irq_reg_stride
),
&
data
->
status_buf
[
b
]);
}
else
{
subreg
=
&
chip
->
sub_reg_offsets
[
b
];
for
(
i
=
0
;
i
<
subreg
->
num_regs
;
i
++
)
{
unsigned
int
offset
=
subreg
->
offset
[
i
];
ret
=
regmap_read
(
map
,
chip
->
status_base
+
offset
,
&
data
->
status_buf
[
offset
]);
if
(
ret
)
break
;
}
}
return
ret
;
}
static
irqreturn_t
regmap_irq_thread
(
int
irq
,
void
*
d
)
static
irqreturn_t
regmap_irq_thread
(
int
irq
,
void
*
d
)
{
{
struct
regmap_irq_chip_data
*
data
=
d
;
struct
regmap_irq_chip_data
*
data
=
d
;
...
@@ -352,11 +380,65 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
...
@@ -352,11 +380,65 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
}
}
/*
/*
* Read in the statuses, using a single bulk read if possible
* Read only registers with active IRQs if the chip has 'main status
* in order to reduce the I/O overheads.
* register'. Else read in the statuses, using a single bulk read if
* possible in order to reduce the I/O overheads.
*/
*/
if
(
!
map
->
use_single_read
&&
map
->
reg_stride
==
1
&&
data
->
irq_reg_stride
==
1
)
{
if
(
chip
->
num_main_regs
)
{
unsigned
int
max_main_bits
;
unsigned
long
size
;
size
=
chip
->
num_regs
*
sizeof
(
unsigned
int
);
max_main_bits
=
(
chip
->
num_main_status_bits
)
?
chip
->
num_main_status_bits
:
chip
->
num_regs
;
/* Clear the status buf as we don't read all status regs */
memset
(
data
->
status_buf
,
0
,
size
);
/* We could support bulk read for main status registers
* but I don't expect to see devices with really many main
* status registers so let's only support single reads for the
* sake of simplicity. and add bulk reads only if needed
*/
for
(
i
=
0
;
i
<
chip
->
num_main_regs
;
i
++
)
{
ret
=
regmap_read
(
map
,
chip
->
main_status
+
(
i
*
map
->
reg_stride
*
data
->
irq_reg_stride
),
&
data
->
main_status_buf
[
i
]);
if
(
ret
)
{
dev_err
(
map
->
dev
,
"Failed to read IRQ status %d
\n
"
,
ret
);
goto
exit
;
}
}
/* Read sub registers with active IRQs */
for
(
i
=
0
;
i
<
chip
->
num_main_regs
;
i
++
)
{
unsigned
int
b
;
const
unsigned
long
mreg
=
data
->
main_status_buf
[
i
];
for_each_set_bit
(
b
,
&
mreg
,
map
->
format
.
val_bytes
*
8
)
{
if
(
i
*
map
->
format
.
val_bytes
*
8
+
b
>
max_main_bits
)
break
;
ret
=
read_sub_irq_data
(
data
,
b
);
if
(
ret
!=
0
)
{
dev_err
(
map
->
dev
,
"Failed to read IRQ status %d
\n
"
,
ret
);
if
(
chip
->
runtime_pm
)
pm_runtime_put
(
map
->
dev
);
goto
exit
;
}
}
}
}
else
if
(
!
map
->
use_single_read
&&
map
->
reg_stride
==
1
&&
data
->
irq_reg_stride
==
1
)
{
u8
*
buf8
=
data
->
status_reg_buf
;
u8
*
buf8
=
data
->
status_reg_buf
;
u16
*
buf16
=
data
->
status_reg_buf
;
u16
*
buf16
=
data
->
status_reg_buf
;
u32
*
buf32
=
data
->
status_reg_buf
;
u32
*
buf32
=
data
->
status_reg_buf
;
...
@@ -521,6 +603,15 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
...
@@ -521,6 +603,15 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
if
(
!
d
)
if
(
!
d
)
return
-
ENOMEM
;
return
-
ENOMEM
;
if
(
chip
->
num_main_regs
)
{
d
->
main_status_buf
=
kcalloc
(
chip
->
num_main_regs
,
sizeof
(
unsigned
int
),
GFP_KERNEL
);
if
(
!
d
->
main_status_buf
)
goto
err_alloc
;
}
d
->
status_buf
=
kcalloc
(
chip
->
num_regs
,
sizeof
(
unsigned
int
),
d
->
status_buf
=
kcalloc
(
chip
->
num_regs
,
sizeof
(
unsigned
int
),
GFP_KERNEL
);
GFP_KERNEL
);
if
(
!
d
->
status_buf
)
if
(
!
d
->
status_buf
)
...
...
include/linux/regmap.h
View file @
66fb181d
...
@@ -1131,11 +1131,37 @@ struct regmap_irq {
...
@@ -1131,11 +1131,37 @@ struct regmap_irq {
.reg_offset = (_id) / (_reg_bits), \
.reg_offset = (_id) / (_reg_bits), \
}
}
#define REGMAP_IRQ_MAIN_REG_OFFSET(arr) \
{ .num_regs = ARRAY_SIZE((arr)), .offset = &(arr)[0] }
struct
regmap_irq_sub_irq_map
{
unsigned
int
num_regs
;
unsigned
int
*
offset
;
};
/**
/**
* struct regmap_irq_chip - Description of a generic regmap irq_chip.
* struct regmap_irq_chip - Description of a generic regmap irq_chip.
*
*
* @name: Descriptive name for IRQ controller.
* @name: Descriptive name for IRQ controller.
*
*
* @main_status: Base main status register address. For chips which have
* interrupts arranged in separate sub-irq blocks with own IRQ
* registers and which have a main IRQ registers indicating
* sub-irq blocks with unhandled interrupts. For such chips fill
* sub-irq register information in status_base, mask_base and
* ack_base.
* @num_main_status_bits: Should be given to chips where number of meaningfull
* main status bits differs from num_regs.
* @sub_reg_offsets: arrays of mappings from main register bits to sub irq
* registers. First item in array describes the registers
* for first main status bit. Second array for second bit etc.
* Offset is given as sub register status offset to
* status_base. Should contain num_regs arrays.
* Can be provided for chips with more complex mapping than
* 1.st bit to 1.st sub-reg, 2.nd bit to 2.nd sub-reg, ...
* @num_main_regs: Number of 'main status' irq registers for chips which have
* main_status set.
*
* @status_base: Base status register address.
* @status_base: Base status register address.
* @mask_base: Base mask register address.
* @mask_base: Base mask register address.
* @mask_writeonly: Base mask register is write only.
* @mask_writeonly: Base mask register is write only.
...
@@ -1181,6 +1207,11 @@ struct regmap_irq {
...
@@ -1181,6 +1207,11 @@ struct regmap_irq {
struct
regmap_irq_chip
{
struct
regmap_irq_chip
{
const
char
*
name
;
const
char
*
name
;
unsigned
int
main_status
;
unsigned
int
num_main_status_bits
;
struct
regmap_irq_sub_irq_map
*
sub_reg_offsets
;
int
num_main_regs
;
unsigned
int
status_base
;
unsigned
int
status_base
;
unsigned
int
mask_base
;
unsigned
int
mask_base
;
unsigned
int
unmask_base
;
unsigned
int
unmask_base
;
...
...
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