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
Kirill Smelkov
linux
Commits
408f110e
Commit
408f110e
authored
Oct 10, 2020
by
Marc Zyngier
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'irq/tegra-pmc' into irq/irqchip-next
Signed-off-by:
Marc Zyngier
<
maz@kernel.org
>
parents
04e8c5b2
c351ab7b
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
150 additions
and
58 deletions
+150
-58
drivers/gpio/gpio-tegra186.c
drivers/gpio/gpio-tegra186.c
+13
-2
drivers/soc/tegra/pmc.c
drivers/soc/tegra/pmc.c
+39
-52
include/linux/irqdomain.h
include/linux/irqdomain.h
+3
-0
kernel/irq/irqdomain.c
kernel/irq/irqdomain.c
+95
-4
No files found.
drivers/gpio/gpio-tegra186.c
View file @
408f110e
...
...
@@ -430,7 +430,18 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type)
else
irq_set_handler_locked
(
data
,
handle_edge_irq
);
return
irq_chip_set_type_parent
(
data
,
type
);
if
(
data
->
parent_data
)
return
irq_chip_set_type_parent
(
data
,
type
);
return
0
;
}
static
int
tegra186_irq_set_wake
(
struct
irq_data
*
data
,
unsigned
int
on
)
{
if
(
data
->
parent_data
)
return
irq_chip_set_wake_parent
(
data
,
on
);
return
0
;
}
static
void
tegra186_gpio_irq
(
struct
irq_desc
*
desc
)
...
...
@@ -678,7 +689,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
gpio
->
intc
.
irq_mask
=
tegra186_irq_mask
;
gpio
->
intc
.
irq_unmask
=
tegra186_irq_unmask
;
gpio
->
intc
.
irq_set_type
=
tegra186_irq_set_type
;
gpio
->
intc
.
irq_set_wake
=
irq_chip_set_wake_parent
;
gpio
->
intc
.
irq_set_wake
=
tegra186_irq_set_wake
;
irq
=
&
gpio
->
gpio
.
irq
;
irq
->
chip
=
&
gpio
->
intc
;
...
...
drivers/soc/tegra/pmc.c
View file @
408f110e
...
...
@@ -1990,44 +1990,17 @@ static int tegra_pmc_irq_alloc(struct irq_domain *domain, unsigned int virq,
event
->
id
,
&
pmc
->
irq
,
pmc
);
/*
* GPIOs don't have an equivalent interrupt in the
* parent controller (GIC). However some code, such
* as the one in irq_get_irqchip_state(), require a
* valid IRQ chip to be set. Make sure that's the
* case by passing NULL here, which will install a
* dummy IRQ chip for the interrupt in the parent
* domain.
*/
if
(
domain
->
parent
)
irq_domain_set_hwirq_and_chip
(
domain
->
parent
,
virq
,
0
,
NULL
,
NULL
);
/* GPIO hierarchies stop at the PMC level */
if
(
!
err
&&
domain
->
parent
)
err
=
irq_domain_disconnect_hierarchy
(
domain
->
parent
,
virq
);
break
;
}
}
/*
* For interrupts that don't have associated wake events, assign a
* dummy hardware IRQ number. This is used in the ->irq_set_type()
* and ->irq_set_wake() callbacks to return early for these IRQs.
*/
if
(
i
==
soc
->
num_wake_events
)
{
err
=
irq_domain_set_hwirq_and_chip
(
domain
,
virq
,
ULONG_MAX
,
&
pmc
->
irq
,
pmc
);
/*
* Interrupts without a wake event don't have a corresponding
* interrupt in the parent controller (GIC). Pass NULL for the
* chip here, which causes a dummy IRQ chip to be installed
* for the interrupt in the parent domain, to make this
* explicit.
*/
if
(
domain
->
parent
)
irq_domain_set_hwirq_and_chip
(
domain
->
parent
,
virq
,
0
,
NULL
,
NULL
);
}
/* If there is no wake-up event, there is no PMC mapping */
if
(
i
==
soc
->
num_wake_events
)
err
=
irq_domain_disconnect_hierarchy
(
domain
,
virq
);
return
err
;
}
...
...
@@ -2043,9 +2016,6 @@ static int tegra210_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
unsigned
int
offset
,
bit
;
u32
value
;
if
(
data
->
hwirq
==
ULONG_MAX
)
return
0
;
offset
=
data
->
hwirq
/
32
;
bit
=
data
->
hwirq
%
32
;
...
...
@@ -2080,9 +2050,6 @@ static int tegra210_pmc_irq_set_type(struct irq_data *data, unsigned int type)
unsigned
int
offset
,
bit
;
u32
value
;
if
(
data
->
hwirq
==
ULONG_MAX
)
return
0
;
offset
=
data
->
hwirq
/
32
;
bit
=
data
->
hwirq
%
32
;
...
...
@@ -2123,10 +2090,6 @@ static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on)
unsigned
int
offset
,
bit
;
u32
value
;
/* nothing to do if there's no associated wake event */
if
(
WARN_ON
(
data
->
hwirq
==
ULONG_MAX
))
return
0
;
offset
=
data
->
hwirq
/
32
;
bit
=
data
->
hwirq
%
32
;
...
...
@@ -2154,10 +2117,6 @@ static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
struct
tegra_pmc
*
pmc
=
irq_data_get_irq_chip_data
(
data
);
u32
value
;
/* nothing to do if there's no associated wake event */
if
(
data
->
hwirq
==
ULONG_MAX
)
return
0
;
value
=
readl
(
pmc
->
wake
+
WAKE_AOWAKE_CNTRL
(
data
->
hwirq
));
switch
(
type
)
{
...
...
@@ -2184,6 +2143,34 @@ static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
return
0
;
}
static
void
tegra_irq_mask_parent
(
struct
irq_data
*
data
)
{
if
(
data
->
parent_data
)
irq_chip_mask_parent
(
data
);
}
static
void
tegra_irq_unmask_parent
(
struct
irq_data
*
data
)
{
if
(
data
->
parent_data
)
irq_chip_unmask_parent
(
data
);
}
static
void
tegra_irq_eoi_parent
(
struct
irq_data
*
data
)
{
if
(
data
->
parent_data
)
irq_chip_eoi_parent
(
data
);
}
static
int
tegra_irq_set_affinity_parent
(
struct
irq_data
*
data
,
const
struct
cpumask
*
dest
,
bool
force
)
{
if
(
data
->
parent_data
)
return
irq_chip_set_affinity_parent
(
data
,
dest
,
force
);
return
-
EINVAL
;
}
static
int
tegra_pmc_irq_init
(
struct
tegra_pmc
*
pmc
)
{
struct
irq_domain
*
parent
=
NULL
;
...
...
@@ -2199,10 +2186,10 @@ static int tegra_pmc_irq_init(struct tegra_pmc *pmc)
return
0
;
pmc
->
irq
.
name
=
dev_name
(
pmc
->
dev
);
pmc
->
irq
.
irq_mask
=
irq_chip
_mask_parent
;
pmc
->
irq
.
irq_unmask
=
irq_chip
_unmask_parent
;
pmc
->
irq
.
irq_eoi
=
irq_chip
_eoi_parent
;
pmc
->
irq
.
irq_set_affinity
=
irq_chip
_set_affinity_parent
;
pmc
->
irq
.
irq_mask
=
tegra_irq
_mask_parent
;
pmc
->
irq
.
irq_unmask
=
tegra_irq
_unmask_parent
;
pmc
->
irq
.
irq_eoi
=
tegra_irq
_eoi_parent
;
pmc
->
irq
.
irq_set_affinity
=
tegra_irq
_set_affinity_parent
;
pmc
->
irq
.
irq_set_type
=
pmc
->
soc
->
irq_set_type
;
pmc
->
irq
.
irq_set_wake
=
pmc
->
soc
->
irq_set_wake
;
...
...
include/linux/irqdomain.h
View file @
408f110e
...
...
@@ -509,6 +509,9 @@ extern void irq_domain_free_irqs_parent(struct irq_domain *domain,
unsigned
int
irq_base
,
unsigned
int
nr_irqs
);
extern
int
irq_domain_disconnect_hierarchy
(
struct
irq_domain
*
domain
,
unsigned
int
virq
);
static
inline
bool
irq_domain_is_hierarchy
(
struct
irq_domain
*
domain
)
{
return
domain
->
flags
&
IRQ_DOMAIN_FLAG_HIERARCHY
;
...
...
kernel/irq/irqdomain.c
View file @
408f110e
...
...
@@ -1136,6 +1136,17 @@ static struct irq_data *irq_domain_insert_irq_data(struct irq_domain *domain,
return
irq_data
;
}
static
void
__irq_domain_free_hierarchy
(
struct
irq_data
*
irq_data
)
{
struct
irq_data
*
tmp
;
while
(
irq_data
)
{
tmp
=
irq_data
;
irq_data
=
irq_data
->
parent_data
;
kfree
(
tmp
);
}
}
static
void
irq_domain_free_irq_data
(
unsigned
int
virq
,
unsigned
int
nr_irqs
)
{
struct
irq_data
*
irq_data
,
*
tmp
;
...
...
@@ -1147,12 +1158,83 @@ static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
irq_data
->
parent_data
=
NULL
;
irq_data
->
domain
=
NULL
;
while
(
tmp
)
{
irq_data
=
tmp
;
tmp
=
tmp
->
parent_data
;
kfree
(
irq_data
);
__irq_domain_free_hierarchy
(
tmp
);
}
}
/**
* irq_domain_disconnect_hierarchy - Mark the first unused level of a hierarchy
* @domain: IRQ domain from which the hierarchy is to be disconnected
* @virq: IRQ number where the hierarchy is to be trimmed
*
* Marks the @virq level belonging to @domain as disconnected.
* Returns -EINVAL if @virq doesn't have a valid irq_data pointing
* to @domain.
*
* Its only use is to be able to trim levels of hierarchy that do not
* have any real meaning for this interrupt, and that the driver marks
* as such from its .alloc() callback.
*/
int
irq_domain_disconnect_hierarchy
(
struct
irq_domain
*
domain
,
unsigned
int
virq
)
{
struct
irq_data
*
irqd
;
irqd
=
irq_domain_get_irq_data
(
domain
,
virq
);
if
(
!
irqd
)
return
-
EINVAL
;
irqd
->
chip
=
ERR_PTR
(
-
ENOTCONN
);
return
0
;
}
static
int
irq_domain_trim_hierarchy
(
unsigned
int
virq
)
{
struct
irq_data
*
tail
,
*
irqd
,
*
irq_data
;
irq_data
=
irq_get_irq_data
(
virq
);
tail
=
NULL
;
/* The first entry must have a valid irqchip */
if
(
!
irq_data
->
chip
||
IS_ERR
(
irq_data
->
chip
))
return
-
EINVAL
;
/*
* Validate that the irq_data chain is sane in the presence of
* a hierarchy trimming marker.
*/
for
(
irqd
=
irq_data
->
parent_data
;
irqd
;
irq_data
=
irqd
,
irqd
=
irqd
->
parent_data
)
{
/* Can't have a valid irqchip after a trim marker */
if
(
irqd
->
chip
&&
tail
)
return
-
EINVAL
;
/* Can't have an empty irqchip before a trim marker */
if
(
!
irqd
->
chip
&&
!
tail
)
return
-
EINVAL
;
if
(
IS_ERR
(
irqd
->
chip
))
{
/* Only -ENOTCONN is a valid trim marker */
if
(
PTR_ERR
(
irqd
->
chip
)
!=
-
ENOTCONN
)
return
-
EINVAL
;
tail
=
irq_data
;
}
}
/* No trim marker, nothing to do */
if
(
!
tail
)
return
0
;
pr_info
(
"IRQ%d: trimming hierarchy from %s
\n
"
,
virq
,
tail
->
parent_data
->
domain
->
name
);
/* Sever the inner part of the hierarchy... */
irqd
=
tail
;
tail
=
tail
->
parent_data
;
irqd
->
parent_data
=
NULL
;
__irq_domain_free_hierarchy
(
tail
);
return
0
;
}
static
int
irq_domain_alloc_irq_data
(
struct
irq_domain
*
domain
,
...
...
@@ -1362,6 +1444,15 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
mutex_unlock
(
&
irq_domain_mutex
);
goto
out_free_irq_data
;
}
for
(
i
=
0
;
i
<
nr_irqs
;
i
++
)
{
ret
=
irq_domain_trim_hierarchy
(
virq
+
i
);
if
(
ret
)
{
mutex_unlock
(
&
irq_domain_mutex
);
goto
out_free_irq_data
;
}
}
for
(
i
=
0
;
i
<
nr_irqs
;
i
++
)
irq_domain_insert_irq
(
virq
+
i
);
mutex_unlock
(
&
irq_domain_mutex
);
...
...
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