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
7e62dd91
Commit
7e62dd91
authored
Sep 17, 2020
by
Marc Zyngier
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/irq/ipi-as-irq' into irq/irqchip-next
Signed-off-by:
Marc Zyngier
<
maz@kernel.org
>
parents
696966ec
8594c3b8
Changes
22
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
769 additions
and
461 deletions
+769
-461
arch/arm/Kconfig
arch/arm/Kconfig
+1
-0
arch/arm/include/asm/hardirq.h
arch/arm/include/asm/hardirq.h
+0
-17
arch/arm/include/asm/smp.h
arch/arm/include/asm/smp.h
+2
-3
arch/arm/kernel/irq.c
arch/arm/kernel/irq.c
+0
-1
arch/arm/kernel/smp.c
arch/arm/kernel/smp.c
+90
-46
arch/arm64/Kconfig
arch/arm64/Kconfig
+1
-0
arch/arm64/include/asm/hardirq.h
arch/arm64/include/asm/hardirq.h
+0
-9
arch/arm64/include/asm/irq_work.h
arch/arm64/include/asm/irq_work.h
+1
-3
arch/arm64/include/asm/smp.h
arch/arm64/include/asm/smp.h
+2
-14
arch/arm64/kernel/irq.c
arch/arm64/kernel/irq.c
+1
-10
arch/arm64/kernel/smp.c
arch/arm64/kernel/smp.c
+83
-44
drivers/irqchip/irq-armada-370-xp.c
drivers/irqchip/irq-armada-370-xp.c
+178
-84
drivers/irqchip/irq-bcm2836.c
drivers/irqchip/irq-bcm2836.c
+127
-26
drivers/irqchip/irq-gic-common.c
drivers/irqchip/irq-gic-common.c
+0
-3
drivers/irqchip/irq-gic-v3.c
drivers/irqchip/irq-gic-v3.c
+66
-46
drivers/irqchip/irq-gic.c
drivers/irqchip/irq-gic.c
+137
-104
drivers/irqchip/irq-hip04.c
drivers/irqchip/irq-hip04.c
+40
-49
include/linux/irq.h
include/linux/irq.h
+4
-1
kernel/irq/chip.c
kernel/irq/chip.c
+27
-0
kernel/irq/debugfs.c
kernel/irq/debugfs.c
+1
-0
kernel/irq/proc.c
kernel/irq/proc.c
+1
-1
kernel/irq/settings.h
kernel/irq/settings.h
+7
-0
No files found.
arch/arm/Kconfig
View file @
7e62dd91
...
...
@@ -49,6 +49,7 @@ config ARM
select GENERIC_ARCH_TOPOLOGY if ARM_CPU_TOPOLOGY
select GENERIC_ATOMIC64 if CPU_V7M || CPU_V6 || !CPU_32v6K || !AEABI
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
select GENERIC_IRQ_IPI if SMP
select GENERIC_CPU_AUTOPROBE
select GENERIC_EARLY_IOREMAP
select GENERIC_IDLE_POLL_SETUP
...
...
arch/arm/include/asm/hardirq.h
View file @
7e62dd91
...
...
@@ -6,29 +6,12 @@
#include <linux/threads.h>
#include <asm/irq.h>
/* number of IPIS _not_ including IPI_CPU_BACKTRACE */
#define NR_IPI 7
typedef
struct
{
unsigned
int
__softirq_pending
;
#ifdef CONFIG_SMP
unsigned
int
ipi_irqs
[
NR_IPI
];
#endif
}
____cacheline_aligned
irq_cpustat_t
;
#include <linux/irq_cpustat.h>
/* Standard mappings for irq_cpustat_t above */
#define __inc_irq_stat(cpu, member) __IRQ_STAT(cpu, member)++
#define __get_irq_stat(cpu, member) __IRQ_STAT(cpu, member)
#ifdef CONFIG_SMP
u64
smp_irq_stat_cpu
(
unsigned
int
cpu
);
#else
#define smp_irq_stat_cpu(cpu) 0
#endif
#define arch_irq_stat_cpu smp_irq_stat_cpu
#define __ARCH_IRQ_EXIT_IRQS_DISABLED 1
#endif
/* __ASM_HARDIRQ_H */
arch/arm/include/asm/smp.h
View file @
7e62dd91
...
...
@@ -39,11 +39,10 @@ void handle_IPI(int ipinr, struct pt_regs *regs);
*/
extern
void
smp_init_cpus
(
void
);
/*
*
Provide a function to raise an IPI cross call on CPUs in callmap.
*
Register IPI interrupts with the arch SMP code
*/
extern
void
set_smp_
cross_call
(
void
(
*
)(
const
struct
cpumask
*
,
unsigned
int
)
);
extern
void
set_smp_
ipi_range
(
int
ipi_base
,
int
nr_ipi
);
/*
* Called from platform specific assembly code, this is the
...
...
arch/arm/kernel/irq.c
View file @
7e62dd91
...
...
@@ -18,7 +18,6 @@
* IRQ's are in fact implemented a bit like signal handlers for the kernel.
* Naturally it's not a 1:1 relation, but there are similarities.
*/
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
...
...
arch/arm/kernel/smp.c
View file @
7e62dd91
...
...
@@ -26,6 +26,7 @@
#include <linux/completion.h>
#include <linux/cpufreq.h>
#include <linux/irq_work.h>
#include <linux/kernel_stat.h>
#include <linux/atomic.h>
#include <asm/bugs.h>
...
...
@@ -65,18 +66,27 @@ enum ipi_msg_type {
IPI_CPU_STOP
,
IPI_IRQ_WORK
,
IPI_COMPLETION
,
NR_IPI
,
/*
* CPU_BACKTRACE is special and not included in NR_IPI
* or tracable with trace_ipi_*
*/
IPI_CPU_BACKTRACE
,
IPI_CPU_BACKTRACE
=
NR_IPI
,
/*
* SGI8-15 can be reserved by secure firmware, and thus may
* not be usable by the kernel. Please keep the above limited
* to at most 8 entries.
*/
MAX_IPI
};
static
int
ipi_irq_base
__read_mostly
;
static
int
nr_ipi
__read_mostly
=
NR_IPI
;
static
struct
irq_desc
*
ipi_desc
[
MAX_IPI
]
__read_mostly
;
static
void
ipi_setup
(
int
cpu
);
static
void
ipi_teardown
(
int
cpu
);
static
DECLARE_COMPLETION
(
cpu_running
);
static
struct
smp_operations
smp_ops
__ro_after_init
;
...
...
@@ -247,6 +257,7 @@ int __cpu_disable(void)
* and we must not schedule until we're ready to give up the cpu.
*/
set_cpu_online
(
cpu
,
false
);
ipi_teardown
(
cpu
);
/*
* OK - migrate IRQs away from this CPU
...
...
@@ -422,6 +433,8 @@ asmlinkage void secondary_start_kernel(void)
notify_cpu_starting
(
cpu
);
ipi_setup
(
cpu
);
calibrate_delay
();
smp_store_cpu_info
(
cpu
);
...
...
@@ -500,14 +513,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
}
}
static
void
(
*
__smp_cross_call
)(
const
struct
cpumask
*
,
unsigned
int
);
void
__init
set_smp_cross_call
(
void
(
*
fn
)(
const
struct
cpumask
*
,
unsigned
int
))
{
if
(
!
__smp_cross_call
)
__smp_cross_call
=
fn
;
}
static
const
char
*
ipi_types
[
NR_IPI
]
__tracepoint_string
=
{
#define S(x,s) [x] = s
S
(
IPI_WAKEUP
,
"CPU wakeup interrupts"
),
...
...
@@ -519,38 +524,23 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = {
S
(
IPI_COMPLETION
,
"completion interrupts"
),
};
static
void
smp_cross_call
(
const
struct
cpumask
*
target
,
unsigned
int
ipinr
)
{
trace_ipi_raise_rcuidle
(
target
,
ipi_types
[
ipinr
]);
__smp_cross_call
(
target
,
ipinr
);
}
static
void
smp_cross_call
(
const
struct
cpumask
*
target
,
unsigned
int
ipinr
);
void
show_ipi_list
(
struct
seq_file
*
p
,
int
prec
)
{
unsigned
int
cpu
,
i
;
for
(
i
=
0
;
i
<
NR_IPI
;
i
++
)
{
unsigned
int
irq
=
irq_desc_get_irq
(
ipi_desc
[
i
]);
seq_printf
(
p
,
"%*s%u: "
,
prec
-
1
,
"IPI"
,
i
);
for_each_online_cpu
(
cpu
)
seq_printf
(
p
,
"%10u "
,
__get_irq_stat
(
cpu
,
ipi_irqs
[
i
]));
seq_printf
(
p
,
"%10u "
,
kstat_irqs_cpu
(
irq
,
cpu
));
seq_printf
(
p
,
" %s
\n
"
,
ipi_types
[
i
]);
}
}
u64
smp_irq_stat_cpu
(
unsigned
int
cpu
)
{
u64
sum
=
0
;
int
i
;
for
(
i
=
0
;
i
<
NR_IPI
;
i
++
)
sum
+=
__get_irq_stat
(
cpu
,
ipi_irqs
[
i
]);
return
sum
;
}
void
arch_send_call_function_ipi_mask
(
const
struct
cpumask
*
mask
)
{
smp_cross_call
(
mask
,
IPI_CALL_FUNC
);
...
...
@@ -627,15 +617,12 @@ asmlinkage void __exception_irq_entry do_IPI(int ipinr, struct pt_regs *regs)
handle_IPI
(
ipinr
,
regs
);
}
void
handle_IPI
(
int
ipinr
,
struct
pt_regs
*
regs
)
static
void
do_handle_IPI
(
int
ipinr
)
{
unsigned
int
cpu
=
smp_processor_id
();
struct
pt_regs
*
old_regs
=
set_irq_regs
(
regs
);
if
((
unsigned
)
ipinr
<
NR_IPI
)
{
if
((
unsigned
)
ipinr
<
NR_IPI
)
trace_ipi_entry_rcuidle
(
ipi_types
[
ipinr
]);
__inc_irq_stat
(
cpu
,
ipi_irqs
[
ipinr
]);
}
switch
(
ipinr
)
{
case
IPI_WAKEUP
:
...
...
@@ -643,9 +630,7 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
case
IPI_TIMER
:
irq_enter
();
tick_receive_broadcast
();
irq_exit
();
break
;
#endif
...
...
@@ -654,36 +639,26 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
break
;
case
IPI_CALL_FUNC
:
irq_enter
();
generic_smp_call_function_interrupt
();
irq_exit
();
break
;
case
IPI_CPU_STOP
:
irq_enter
();
ipi_cpu_stop
(
cpu
);
irq_exit
();
break
;
#ifdef CONFIG_IRQ_WORK
case
IPI_IRQ_WORK
:
irq_enter
();
irq_work_run
();
irq_exit
();
break
;
#endif
case
IPI_COMPLETION
:
irq_enter
();
ipi_complete
(
cpu
);
irq_exit
();
break
;
case
IPI_CPU_BACKTRACE
:
printk_nmi_enter
();
irq_enter
();
nmi_cpu_backtrace
(
regs
);
irq_exit
();
nmi_cpu_backtrace
(
get_irq_regs
());
printk_nmi_exit
();
break
;
...
...
@@ -695,9 +670,78 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
if
((
unsigned
)
ipinr
<
NR_IPI
)
trace_ipi_exit_rcuidle
(
ipi_types
[
ipinr
]);
}
/* Legacy version, should go away once all irqchips have been converted */
void
handle_IPI
(
int
ipinr
,
struct
pt_regs
*
regs
)
{
struct
pt_regs
*
old_regs
=
set_irq_regs
(
regs
);
irq_enter
();
do_handle_IPI
(
ipinr
);
irq_exit
();
set_irq_regs
(
old_regs
);
}
static
irqreturn_t
ipi_handler
(
int
irq
,
void
*
data
)
{
do_handle_IPI
(
irq
-
ipi_irq_base
);
return
IRQ_HANDLED
;
}
static
void
smp_cross_call
(
const
struct
cpumask
*
target
,
unsigned
int
ipinr
)
{
trace_ipi_raise_rcuidle
(
target
,
ipi_types
[
ipinr
]);
__ipi_send_mask
(
ipi_desc
[
ipinr
],
target
);
}
static
void
ipi_setup
(
int
cpu
)
{
int
i
;
if
(
WARN_ON_ONCE
(
!
ipi_irq_base
))
return
;
for
(
i
=
0
;
i
<
nr_ipi
;
i
++
)
enable_percpu_irq
(
ipi_irq_base
+
i
,
0
);
}
static
void
ipi_teardown
(
int
cpu
)
{
int
i
;
if
(
WARN_ON_ONCE
(
!
ipi_irq_base
))
return
;
for
(
i
=
0
;
i
<
nr_ipi
;
i
++
)
disable_percpu_irq
(
ipi_irq_base
+
i
);
}
void
__init
set_smp_ipi_range
(
int
ipi_base
,
int
n
)
{
int
i
;
WARN_ON
(
n
<
MAX_IPI
);
nr_ipi
=
min
(
n
,
MAX_IPI
);
for
(
i
=
0
;
i
<
nr_ipi
;
i
++
)
{
int
err
;
err
=
request_percpu_irq
(
ipi_base
+
i
,
ipi_handler
,
"IPI"
,
&
irq_stat
);
WARN_ON
(
err
);
ipi_desc
[
i
]
=
irq_to_desc
(
ipi_base
+
i
);
irq_set_status_flags
(
ipi_base
+
i
,
IRQ_HIDDEN
);
}
ipi_irq_base
=
ipi_base
;
/* Setup the boot CPU immediately */
ipi_setup
(
smp_processor_id
());
}
void
smp_send_reschedule
(
int
cpu
)
{
smp_cross_call
(
cpumask_of
(
cpu
),
IPI_RESCHEDULE
);
...
...
@@ -805,7 +849,7 @@ core_initcall(register_cpufreq_notifier);
static
void
raise_nmi
(
cpumask_t
*
mask
)
{
__
smp_cross_call
(
mask
,
IPI_CPU_BACKTRACE
);
__
ipi_send_mask
(
ipi_desc
[
IPI_CPU_BACKTRACE
],
mask
);
}
void
arch_trigger_cpumask_backtrace
(
const
cpumask_t
*
mask
,
bool
exclude_self
)
...
...
arch/arm64/Kconfig
View file @
7e62dd91
...
...
@@ -106,6 +106,7 @@ config ARM64
select GENERIC_CPU_VULNERABILITIES
select GENERIC_EARLY_IOREMAP
select GENERIC_IDLE_POLL_SETUP
select GENERIC_IRQ_IPI
select GENERIC_IRQ_MULTI_HANDLER
select GENERIC_IRQ_PROBE
select GENERIC_IRQ_SHOW
...
...
arch/arm64/include/asm/hardirq.h
View file @
7e62dd91
...
...
@@ -13,21 +13,12 @@
#include <asm/kvm_arm.h>
#include <asm/sysreg.h>
#define NR_IPI 7
typedef
struct
{
unsigned
int
__softirq_pending
;
unsigned
int
ipi_irqs
[
NR_IPI
];
}
____cacheline_aligned
irq_cpustat_t
;
#include <linux/irq_cpustat.h>
/* Standard mappings for irq_cpustat_t above */
#define __inc_irq_stat(cpu, member) __IRQ_STAT(cpu, member)++
#define __get_irq_stat(cpu, member) __IRQ_STAT(cpu, member)
u64
smp_irq_stat_cpu
(
unsigned
int
cpu
);
#define arch_irq_stat_cpu smp_irq_stat_cpu
#define __ARCH_IRQ_EXIT_IRQS_DISABLED 1
struct
nmi_ctx
{
...
...
arch/arm64/include/asm/irq_work.h
View file @
7e62dd91
...
...
@@ -2,11 +2,9 @@
#ifndef __ASM_IRQ_WORK_H
#define __ASM_IRQ_WORK_H
#include <asm/smp.h>
static
inline
bool
arch_irq_work_has_interrupt
(
void
)
{
return
!!
__smp_cross_call
;
return
true
;
}
#endif
/* __ASM_IRQ_WORK_H */
arch/arm64/include/asm/smp.h
View file @
7e62dd91
...
...
@@ -55,16 +55,6 @@ static inline void set_cpu_logical_map(int cpu, u64 hwid)
struct
seq_file
;
/*
* generate IPI list text
*/
extern
void
show_ipi_list
(
struct
seq_file
*
p
,
int
prec
);
/*
* Called from C code, this handles an IPI.
*/
extern
void
handle_IPI
(
int
ipinr
,
struct
pt_regs
*
regs
);
/*
* Discover the set of possible CPUs and determine their
* SMP operations.
...
...
@@ -72,11 +62,9 @@ extern void handle_IPI(int ipinr, struct pt_regs *regs);
extern
void
smp_init_cpus
(
void
);
/*
*
Provide a function to raise an IPI cross call on CPUs in callmap.
*
Register IPI interrupts with the arch SMP code
*/
extern
void
set_smp_cross_call
(
void
(
*
)(
const
struct
cpumask
*
,
unsigned
int
));
extern
void
(
*
__smp_cross_call
)(
const
struct
cpumask
*
,
unsigned
int
);
extern
void
set_smp_ipi_range
(
int
ipi_base
,
int
nr_ipi
);
/*
* Called from the secondary holding pen, this is the secondary CPU entry point.
...
...
arch/arm64/kernel/irq.c
View file @
7e62dd91
...
...
@@ -10,10 +10,10 @@
* Copyright (C) 2012 ARM Ltd.
*/
#include <linux/kernel_stat.h>
#include <linux/irq.h>
#include <linux/memory.h>
#include <linux/smp.h>
#include <linux/hardirq.h>
#include <linux/init.h>
#include <linux/irqchip.h>
#include <linux/kprobes.h>
...
...
@@ -22,20 +22,11 @@
#include <asm/daifflags.h>
#include <asm/vmap_stack.h>
unsigned
long
irq_err_count
;
/* Only access this in an NMI enter/exit */
DEFINE_PER_CPU
(
struct
nmi_ctx
,
nmi_contexts
);
DEFINE_PER_CPU
(
unsigned
long
*
,
irq_stack_ptr
);
int
arch_show_interrupts
(
struct
seq_file
*
p
,
int
prec
)
{
show_ipi_list
(
p
,
prec
);
seq_printf
(
p
,
"%*s: %10lu
\n
"
,
prec
,
"Err"
,
irq_err_count
);
return
0
;
}
#ifdef CONFIG_VMAP_STACK
static
void
init_irq_stacks
(
void
)
{
...
...
arch/arm64/kernel/smp.c
View file @
7e62dd91
...
...
@@ -30,6 +30,7 @@
#include <linux/completion.h>
#include <linux/of.h>
#include <linux/irq_work.h>
#include <linux/kernel_stat.h>
#include <linux/kexec.h>
#include <linux/kvm_host.h>
...
...
@@ -72,9 +73,17 @@ enum ipi_msg_type {
IPI_CPU_CRASH_STOP
,
IPI_TIMER
,
IPI_IRQ_WORK
,
IPI_WAKEUP
IPI_WAKEUP
,
NR_IPI
};
static
int
ipi_irq_base
__read_mostly
;
static
int
nr_ipi
__read_mostly
=
NR_IPI
;
static
struct
irq_desc
*
ipi_desc
[
NR_IPI
]
__read_mostly
;
static
void
ipi_setup
(
int
cpu
);
static
void
ipi_teardown
(
int
cpu
);
#ifdef CONFIG_HOTPLUG_CPU
static
int
op_cpu_kill
(
unsigned
int
cpu
);
#else
...
...
@@ -237,6 +246,8 @@ asmlinkage notrace void secondary_start_kernel(void)
*/
notify_cpu_starting
(
cpu
);
ipi_setup
(
cpu
);
store_cpu_topology
(
cpu
);
numa_add_cpu
(
cpu
);
...
...
@@ -302,6 +313,7 @@ int __cpu_disable(void)
* and we must not schedule until we're ready to give up the cpu.
*/
set_cpu_online
(
cpu
,
false
);
ipi_teardown
(
cpu
);
/*
* OK - migrate IRQs away from this CPU
...
...
@@ -772,13 +784,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
}
}
void
(
*
__smp_cross_call
)(
const
struct
cpumask
*
,
unsigned
int
);
void
__init
set_smp_cross_call
(
void
(
*
fn
)(
const
struct
cpumask
*
,
unsigned
int
))
{
__smp_cross_call
=
fn
;
}
static
const
char
*
ipi_types
[
NR_IPI
]
__tracepoint_string
=
{
#define S(x,s) [x] = s
S
(
IPI_RESCHEDULE
,
"Rescheduling interrupts"
),
...
...
@@ -790,35 +795,25 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = {
S
(
IPI_WAKEUP
,
"CPU wake-up interrupts"
),
};
static
void
smp_cross_call
(
const
struct
cpumask
*
target
,
unsigned
int
ipinr
)
{
trace_ipi_raise
(
target
,
ipi_types
[
ipinr
]);
__smp_cross_call
(
target
,
ipinr
);
}
static
void
smp_cross_call
(
const
struct
cpumask
*
target
,
unsigned
int
ipinr
);
void
show_ipi_list
(
struct
seq_file
*
p
,
int
prec
)
unsigned
long
irq_err_count
;
int
arch_show_interrupts
(
struct
seq_file
*
p
,
int
prec
)
{
unsigned
int
cpu
,
i
;
for
(
i
=
0
;
i
<
NR_IPI
;
i
++
)
{
unsigned
int
irq
=
irq_desc_get_irq
(
ipi_desc
[
i
]);
seq_printf
(
p
,
"%*s%u:%s"
,
prec
-
1
,
"IPI"
,
i
,
prec
>=
4
?
" "
:
""
);
for_each_online_cpu
(
cpu
)
seq_printf
(
p
,
"%10u "
,
__get_irq_stat
(
cpu
,
ipi_irqs
[
i
]));
seq_printf
(
p
,
"%10u "
,
kstat_irqs_cpu
(
irq
,
cpu
));
seq_printf
(
p
,
" %s
\n
"
,
ipi_types
[
i
]);
}
}
u64
smp_irq_stat_cpu
(
unsigned
int
cpu
)
{
u64
sum
=
0
;
int
i
;
for
(
i
=
0
;
i
<
NR_IPI
;
i
++
)
sum
+=
__get_irq_stat
(
cpu
,
ipi_irqs
[
i
]);
return
sum
;
seq_printf
(
p
,
"%*s: %10lu
\n
"
,
prec
,
"Err"
,
irq_err_count
);
return
0
;
}
void
arch_send_call_function_ipi_mask
(
const
struct
cpumask
*
mask
)
...
...
@@ -841,8 +836,7 @@ void arch_send_wakeup_ipi_mask(const struct cpumask *mask)
#ifdef CONFIG_IRQ_WORK
void
arch_irq_work_raise
(
void
)
{
if
(
__smp_cross_call
)
smp_cross_call
(
cpumask_of
(
smp_processor_id
()),
IPI_IRQ_WORK
);
smp_cross_call
(
cpumask_of
(
smp_processor_id
()),
IPI_IRQ_WORK
);
}
#endif
...
...
@@ -890,15 +884,12 @@ static void ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs)
/*
* Main handler for inter-processor interrupts
*/
void
handle_IPI
(
int
ipinr
,
struct
pt_regs
*
regs
)
static
void
do_handle_IPI
(
int
ipinr
)
{
unsigned
int
cpu
=
smp_processor_id
();
struct
pt_regs
*
old_regs
=
set_irq_regs
(
regs
);
if
((
unsigned
)
ipinr
<
NR_IPI
)
{
if
((
unsigned
)
ipinr
<
NR_IPI
)
trace_ipi_entry_rcuidle
(
ipi_types
[
ipinr
]);
__inc_irq_stat
(
cpu
,
ipi_irqs
[
ipinr
]);
}
switch
(
ipinr
)
{
case
IPI_RESCHEDULE
:
...
...
@@ -906,21 +897,16 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
break
;
case
IPI_CALL_FUNC
:
irq_enter
();
generic_smp_call_function_interrupt
();
irq_exit
();
break
;
case
IPI_CPU_STOP
:
irq_enter
();
local_cpu_stop
();
irq_exit
();
break
;
case
IPI_CPU_CRASH_STOP
:
if
(
IS_ENABLED
(
CONFIG_KEXEC_CORE
))
{
irq_enter
();
ipi_cpu_crash_stop
(
cpu
,
regs
);
ipi_cpu_crash_stop
(
cpu
,
get_irq_regs
());
unreachable
();
}
...
...
@@ -928,17 +914,13 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
case
IPI_TIMER
:
irq_enter
();
tick_receive_broadcast
();
irq_exit
();
break
;
#endif
#ifdef CONFIG_IRQ_WORK
case
IPI_IRQ_WORK
:
irq_enter
();
irq_work_run
();
irq_exit
();
break
;
#endif
...
...
@@ -957,7 +939,64 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
if
((
unsigned
)
ipinr
<
NR_IPI
)
trace_ipi_exit_rcuidle
(
ipi_types
[
ipinr
]);
set_irq_regs
(
old_regs
);
}
static
irqreturn_t
ipi_handler
(
int
irq
,
void
*
data
)
{
do_handle_IPI
(
irq
-
ipi_irq_base
);
return
IRQ_HANDLED
;
}
static
void
smp_cross_call
(
const
struct
cpumask
*
target
,
unsigned
int
ipinr
)
{
trace_ipi_raise
(
target
,
ipi_types
[
ipinr
]);
__ipi_send_mask
(
ipi_desc
[
ipinr
],
target
);
}
static
void
ipi_setup
(
int
cpu
)
{
int
i
;
if
(
WARN_ON_ONCE
(
!
ipi_irq_base
))
return
;
for
(
i
=
0
;
i
<
nr_ipi
;
i
++
)
enable_percpu_irq
(
ipi_irq_base
+
i
,
0
);
}
static
void
ipi_teardown
(
int
cpu
)
{
int
i
;
if
(
WARN_ON_ONCE
(
!
ipi_irq_base
))
return
;
for
(
i
=
0
;
i
<
nr_ipi
;
i
++
)
disable_percpu_irq
(
ipi_irq_base
+
i
);
}
void
__init
set_smp_ipi_range
(
int
ipi_base
,
int
n
)
{
int
i
;
WARN_ON
(
n
<
NR_IPI
);
nr_ipi
=
min
(
n
,
NR_IPI
);
for
(
i
=
0
;
i
<
nr_ipi
;
i
++
)
{
int
err
;
err
=
request_percpu_irq
(
ipi_base
+
i
,
ipi_handler
,
"IPI"
,
&
cpu_number
);
WARN_ON
(
err
);
ipi_desc
[
i
]
=
irq_to_desc
(
ipi_base
+
i
);
irq_set_status_flags
(
ipi_base
+
i
,
IRQ_HIDDEN
);
}
ipi_irq_base
=
ipi_base
;
/* Setup the boot CPU immediately */
ipi_setup
(
smp_processor_id
());
}
void
smp_send_reschedule
(
int
cpu
)
...
...
drivers/irqchip/irq-armada-370-xp.c
View file @
7e62dd91
...
...
@@ -310,7 +310,134 @@ static inline int armada_370_xp_msi_init(struct device_node *node,
}
#endif
static
void
armada_xp_mpic_perf_init
(
void
)
{
unsigned
long
cpuid
=
cpu_logical_map
(
smp_processor_id
());
/* Enable Performance Counter Overflow interrupts */
writel
(
ARMADA_370_XP_INT_CAUSE_PERF
(
cpuid
),
per_cpu_int_base
+
ARMADA_370_XP_INT_FABRIC_MASK_OFFS
);
}
#ifdef CONFIG_SMP
static
struct
irq_domain
*
ipi_domain
;
static
void
armada_370_xp_ipi_mask
(
struct
irq_data
*
d
)
{
u32
reg
;
reg
=
readl
(
per_cpu_int_base
+
ARMADA_370_XP_IN_DRBEL_MSK_OFFS
);
reg
&=
~
BIT
(
d
->
hwirq
);
writel
(
reg
,
per_cpu_int_base
+
ARMADA_370_XP_IN_DRBEL_MSK_OFFS
);
}
static
void
armada_370_xp_ipi_unmask
(
struct
irq_data
*
d
)
{
u32
reg
;
reg
=
readl
(
per_cpu_int_base
+
ARMADA_370_XP_IN_DRBEL_MSK_OFFS
);
reg
|=
BIT
(
d
->
hwirq
);
writel
(
reg
,
per_cpu_int_base
+
ARMADA_370_XP_IN_DRBEL_MSK_OFFS
);
}
static
void
armada_370_xp_ipi_send_mask
(
struct
irq_data
*
d
,
const
struct
cpumask
*
mask
)
{
unsigned
long
map
=
0
;
int
cpu
;
/* Convert our logical CPU mask into a physical one. */
for_each_cpu
(
cpu
,
mask
)
map
|=
1
<<
cpu_logical_map
(
cpu
);
/*
* Ensure that stores to Normal memory are visible to the
* other CPUs before issuing the IPI.
*/
dsb
();
/* submit softirq */
writel
((
map
<<
8
)
|
d
->
hwirq
,
main_int_base
+
ARMADA_370_XP_SW_TRIG_INT_OFFS
);
}
static
void
armada_370_xp_ipi_eoi
(
struct
irq_data
*
d
)
{
writel
(
~
BIT
(
d
->
hwirq
),
per_cpu_int_base
+
ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS
);
}
static
struct
irq_chip
ipi_irqchip
=
{
.
name
=
"IPI"
,
.
irq_mask
=
armada_370_xp_ipi_mask
,
.
irq_unmask
=
armada_370_xp_ipi_unmask
,
.
irq_eoi
=
armada_370_xp_ipi_eoi
,
.
ipi_send_mask
=
armada_370_xp_ipi_send_mask
,
};
static
int
armada_370_xp_ipi_alloc
(
struct
irq_domain
*
d
,
unsigned
int
virq
,
unsigned
int
nr_irqs
,
void
*
args
)
{
int
i
;
for
(
i
=
0
;
i
<
nr_irqs
;
i
++
)
{
irq_set_percpu_devid
(
virq
+
i
);
irq_domain_set_info
(
d
,
virq
+
i
,
i
,
&
ipi_irqchip
,
d
->
host_data
,
handle_percpu_devid_fasteoi_ipi
,
NULL
,
NULL
);
}
return
0
;
}
static
void
armada_370_xp_ipi_free
(
struct
irq_domain
*
d
,
unsigned
int
virq
,
unsigned
int
nr_irqs
)
{
/* Not freeing IPIs */
}
static
const
struct
irq_domain_ops
ipi_domain_ops
=
{
.
alloc
=
armada_370_xp_ipi_alloc
,
.
free
=
armada_370_xp_ipi_free
,
};
static
void
ipi_resume
(
void
)
{
int
i
;
for
(
i
=
0
;
i
<
IPI_DOORBELL_END
;
i
++
)
{
int
irq
;
irq
=
irq_find_mapping
(
ipi_domain
,
i
);
if
(
irq
<=
0
)
continue
;
if
(
irq_percpu_is_enabled
(
irq
))
{
struct
irq_data
*
d
;
d
=
irq_domain_get_irq_data
(
ipi_domain
,
irq
);
armada_370_xp_ipi_unmask
(
d
);
}
}
}
static
__init
void
armada_xp_ipi_init
(
struct
device_node
*
node
)
{
int
base_ipi
;
ipi_domain
=
irq_domain_create_linear
(
of_node_to_fwnode
(
node
),
IPI_DOORBELL_END
,
&
ipi_domain_ops
,
NULL
);
if
(
WARN_ON
(
!
ipi_domain
))
return
;
irq_domain_update_bus_token
(
ipi_domain
,
DOMAIN_BUS_IPI
);
base_ipi
=
__irq_domain_alloc_irqs
(
ipi_domain
,
-
1
,
IPI_DOORBELL_END
,
NUMA_NO_NODE
,
NULL
,
false
,
NULL
);
if
(
WARN_ON
(
!
base_ipi
))
return
;
set_smp_ipi_range
(
base_ipi
,
IPI_DOORBELL_END
);
}
static
DEFINE_RAW_SPINLOCK
(
irq_controller_lock
);
static
int
armada_xp_set_affinity
(
struct
irq_data
*
d
,
...
...
@@ -334,43 +461,6 @@ static int armada_xp_set_affinity(struct irq_data *d,
return
IRQ_SET_MASK_OK
;
}
#endif
static
struct
irq_chip
armada_370_xp_irq_chip
=
{
.
name
=
"MPIC"
,
.
irq_mask
=
armada_370_xp_irq_mask
,
.
irq_mask_ack
=
armada_370_xp_irq_mask
,
.
irq_unmask
=
armada_370_xp_irq_unmask
,
#ifdef CONFIG_SMP
.
irq_set_affinity
=
armada_xp_set_affinity
,
#endif
.
flags
=
IRQCHIP_SKIP_SET_WAKE
|
IRQCHIP_MASK_ON_SUSPEND
,
};
static
int
armada_370_xp_mpic_irq_map
(
struct
irq_domain
*
h
,
unsigned
int
virq
,
irq_hw_number_t
hw
)
{
armada_370_xp_irq_mask
(
irq_get_irq_data
(
virq
));
if
(
!
is_percpu_irq
(
hw
))
writel
(
hw
,
per_cpu_int_base
+
ARMADA_370_XP_INT_CLEAR_MASK_OFFS
);
else
writel
(
hw
,
main_int_base
+
ARMADA_370_XP_INT_SET_ENABLE_OFFS
);
irq_set_status_flags
(
virq
,
IRQ_LEVEL
);
if
(
is_percpu_irq
(
hw
))
{
irq_set_percpu_devid
(
virq
);
irq_set_chip_and_handler
(
virq
,
&
armada_370_xp_irq_chip
,
handle_percpu_devid_irq
);
}
else
{
irq_set_chip_and_handler
(
virq
,
&
armada_370_xp_irq_chip
,
handle_level_irq
);
irqd_set_single_target
(
irq_desc_get_irq_data
(
irq_to_desc
(
virq
)));
}
irq_set_probe
(
virq
);
return
0
;
}
static
void
armada_xp_mpic_smp_cpu_init
(
void
)
{
...
...
@@ -383,48 +473,16 @@ static void armada_xp_mpic_smp_cpu_init(void)
for
(
i
=
0
;
i
<
nr_irqs
;
i
++
)
writel
(
i
,
per_cpu_int_base
+
ARMADA_370_XP_INT_SET_MASK_OFFS
);
/* Disable all IPIs */
writel
(
0
,
per_cpu_int_base
+
ARMADA_370_XP_IN_DRBEL_MSK_OFFS
);
/* Clear pending IPIs */
writel
(
0
,
per_cpu_int_base
+
ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS
);
/* Enable first 8 IPIs */
writel
(
IPI_DOORBELL_MASK
,
per_cpu_int_base
+
ARMADA_370_XP_IN_DRBEL_MSK_OFFS
);
/* Unmask IPI interrupt */
writel
(
0
,
per_cpu_int_base
+
ARMADA_370_XP_INT_CLEAR_MASK_OFFS
);
}
static
void
armada_xp_mpic_perf_init
(
void
)
{
unsigned
long
cpuid
=
cpu_logical_map
(
smp_processor_id
());
/* Enable Performance Counter Overflow interrupts */
writel
(
ARMADA_370_XP_INT_CAUSE_PERF
(
cpuid
),
per_cpu_int_base
+
ARMADA_370_XP_INT_FABRIC_MASK_OFFS
);
}
#ifdef CONFIG_SMP
static
void
armada_mpic_send_doorbell
(
const
struct
cpumask
*
mask
,
unsigned
int
irq
)
{
int
cpu
;
unsigned
long
map
=
0
;
/* Convert our logical CPU mask into a physical one. */
for_each_cpu
(
cpu
,
mask
)
map
|=
1
<<
cpu_logical_map
(
cpu
);
/*
* Ensure that stores to Normal memory are visible to the
* other CPUs before issuing the IPI.
*/
dsb
();
/* submit softirq */
writel
((
map
<<
8
)
|
irq
,
main_int_base
+
ARMADA_370_XP_SW_TRIG_INT_OFFS
);
}
static
void
armada_xp_mpic_reenable_percpu
(
void
)
{
unsigned
int
irq
;
...
...
@@ -445,6 +503,8 @@ static void armada_xp_mpic_reenable_percpu(void)
armada_370_xp_irq_unmask
(
data
);
}
ipi_resume
();
}
static
int
armada_xp_mpic_starting_cpu
(
unsigned
int
cpu
)
...
...
@@ -462,7 +522,46 @@ static int mpic_cascaded_starting_cpu(unsigned int cpu)
enable_percpu_irq
(
parent_irq
,
IRQ_TYPE_NONE
);
return
0
;
}
#else
static
void
armada_xp_mpic_smp_cpu_init
(
void
)
{}
static
void
ipi_resume
(
void
)
{}
#endif
static
struct
irq_chip
armada_370_xp_irq_chip
=
{
.
name
=
"MPIC"
,
.
irq_mask
=
armada_370_xp_irq_mask
,
.
irq_mask_ack
=
armada_370_xp_irq_mask
,
.
irq_unmask
=
armada_370_xp_irq_unmask
,
#ifdef CONFIG_SMP
.
irq_set_affinity
=
armada_xp_set_affinity
,
#endif
.
flags
=
IRQCHIP_SKIP_SET_WAKE
|
IRQCHIP_MASK_ON_SUSPEND
,
};
static
int
armada_370_xp_mpic_irq_map
(
struct
irq_domain
*
h
,
unsigned
int
virq
,
irq_hw_number_t
hw
)
{
armada_370_xp_irq_mask
(
irq_get_irq_data
(
virq
));
if
(
!
is_percpu_irq
(
hw
))
writel
(
hw
,
per_cpu_int_base
+
ARMADA_370_XP_INT_CLEAR_MASK_OFFS
);
else
writel
(
hw
,
main_int_base
+
ARMADA_370_XP_INT_SET_ENABLE_OFFS
);
irq_set_status_flags
(
virq
,
IRQ_LEVEL
);
if
(
is_percpu_irq
(
hw
))
{
irq_set_percpu_devid
(
virq
);
irq_set_chip_and_handler
(
virq
,
&
armada_370_xp_irq_chip
,
handle_percpu_devid_irq
);
}
else
{
irq_set_chip_and_handler
(
virq
,
&
armada_370_xp_irq_chip
,
handle_level_irq
);
irqd_set_single_target
(
irq_desc_get_irq_data
(
irq_to_desc
(
virq
)));
}
irq_set_probe
(
virq
);
return
0
;
}
static
const
struct
irq_domain_ops
armada_370_xp_mpic_irq_ops
=
{
.
map
=
armada_370_xp_mpic_irq_map
,
...
...
@@ -562,22 +661,15 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
#ifdef CONFIG_SMP
/* IPI Handling */
if
(
irqnr
==
0
)
{
u32
ipimask
,
ipinr
;
unsigned
long
ipimask
;
int
ipi
;
ipimask
=
readl_relaxed
(
per_cpu_int_base
+
ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS
)
&
IPI_DOORBELL_MASK
;
writel
(
~
ipimask
,
per_cpu_int_base
+
ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS
);
/* Handle all pending doorbells */
for
(
ipinr
=
IPI_DOORBELL_START
;
ipinr
<
IPI_DOORBELL_END
;
ipinr
++
)
{
if
(
ipimask
&
(
0x1
<<
ipinr
))
handle_IPI
(
ipinr
,
regs
);
}
continue
;
for_each_set_bit
(
ipi
,
&
ipimask
,
IPI_DOORBELL_END
)
handle_domain_irq
(
ipi_domain
,
ipi
,
regs
);
}
#endif
...
...
@@ -636,6 +728,8 @@ static void armada_370_xp_mpic_resume(void)
writel
(
0
,
per_cpu_int_base
+
ARMADA_370_XP_INT_CLEAR_MASK_OFFS
);
if
(
doorbell_mask_reg
&
PCI_MSI_DOORBELL_MASK
)
writel
(
1
,
per_cpu_int_base
+
ARMADA_370_XP_INT_CLEAR_MASK_OFFS
);
ipi_resume
();
}
static
struct
syscore_ops
armada_370_xp_mpic_syscore_ops
=
{
...
...
@@ -691,7 +785,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
irq_set_default_host
(
armada_370_xp_mpic_domain
);
set_handle_irq
(
armada_370_xp_handle_irq
);
#ifdef CONFIG_SMP
set_smp_cross_call
(
armada_mpic_send_doorbell
);
armada_xp_ipi_init
(
node
);
cpuhp_setup_state_nocalls
(
CPUHP_AP_IRQ_ARMADA_XP_STARTING
,
"irqchip/armada/ipi:starting"
,
armada_xp_mpic_starting_cpu
,
NULL
);
...
...
drivers/irqchip/irq-bcm2836.c
View file @
7e62dd91
...
...
@@ -10,6 +10,7 @@
#include <linux/of_irq.h>
#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/irq-bcm2836.h>
#include <asm/exception.h>
...
...
@@ -89,12 +90,24 @@ static struct irq_chip bcm2836_arm_irqchip_gpu = {
.
irq_unmask
=
bcm2836_arm_irqchip_unmask_gpu_irq
,
};
static
void
bcm2836_arm_irqchip_dummy_op
(
struct
irq_data
*
d
)
{
}
static
struct
irq_chip
bcm2836_arm_irqchip_dummy
=
{
.
name
=
"bcm2836-dummy"
,
.
irq_eoi
=
bcm2836_arm_irqchip_dummy_op
,
};
static
int
bcm2836_map
(
struct
irq_domain
*
d
,
unsigned
int
irq
,
irq_hw_number_t
hw
)
{
struct
irq_chip
*
chip
;
switch
(
hw
)
{
case
LOCAL_IRQ_MAILBOX0
:
chip
=
&
bcm2836_arm_irqchip_dummy
;
break
;
case
LOCAL_IRQ_CNTPSIRQ
:
case
LOCAL_IRQ_CNTPNSIRQ
:
case
LOCAL_IRQ_CNTHPIRQ
:
...
...
@@ -127,17 +140,7 @@ __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs)
u32
stat
;
stat
=
readl_relaxed
(
intc
.
base
+
LOCAL_IRQ_PENDING0
+
4
*
cpu
);
if
(
stat
&
BIT
(
LOCAL_IRQ_MAILBOX0
))
{
#ifdef CONFIG_SMP
void
__iomem
*
mailbox0
=
(
intc
.
base
+
LOCAL_MAILBOX0_CLR0
+
16
*
cpu
);
u32
mbox_val
=
readl
(
mailbox0
);
u32
ipi
=
ffs
(
mbox_val
)
-
1
;
writel
(
1
<<
ipi
,
mailbox0
);
handle_IPI
(
ipi
,
regs
);
#endif
}
else
if
(
stat
)
{
if
(
stat
)
{
u32
hwirq
=
ffs
(
stat
)
-
1
;
handle_domain_irq
(
intc
.
domain
,
hwirq
,
regs
);
...
...
@@ -145,8 +148,35 @@ __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs)
}
#ifdef CONFIG_SMP
static
void
bcm2836_arm_irqchip_send_ipi
(
const
struct
cpumask
*
mask
,
unsigned
int
ipi
)
static
struct
irq_domain
*
ipi_domain
;
static
void
bcm2836_arm_irqchip_handle_ipi
(
struct
irq_desc
*
desc
)
{
struct
irq_chip
*
chip
=
irq_desc_get_chip
(
desc
);
int
cpu
=
smp_processor_id
();
u32
mbox_val
;
chained_irq_enter
(
chip
,
desc
);
mbox_val
=
readl_relaxed
(
intc
.
base
+
LOCAL_MAILBOX0_CLR0
+
16
*
cpu
);
if
(
mbox_val
)
{
int
hwirq
=
ffs
(
mbox_val
)
-
1
;
generic_handle_irq
(
irq_find_mapping
(
ipi_domain
,
hwirq
));
}
chained_irq_exit
(
chip
,
desc
);
}
static
void
bcm2836_arm_irqchip_ipi_eoi
(
struct
irq_data
*
d
)
{
int
cpu
=
smp_processor_id
();
writel_relaxed
(
BIT
(
d
->
hwirq
),
intc
.
base
+
LOCAL_MAILBOX0_CLR0
+
16
*
cpu
);
}
static
void
bcm2836_arm_irqchip_ipi_send_mask
(
struct
irq_data
*
d
,
const
struct
cpumask
*
mask
)
{
int
cpu
;
void
__iomem
*
mailbox0_base
=
intc
.
base
+
LOCAL_MAILBOX0_SET0
;
...
...
@@ -157,11 +187,47 @@ static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask,
*/
smp_wmb
();
for_each_cpu
(
cpu
,
mask
)
{
writel
(
1
<<
ipi
,
mailbox0_base
+
16
*
cpu
);
for_each_cpu
(
cpu
,
mask
)
writel_relaxed
(
BIT
(
d
->
hwirq
),
mailbox0_base
+
16
*
cpu
);
}
static
struct
irq_chip
bcm2836_arm_irqchip_ipi
=
{
.
name
=
"IPI"
,
.
irq_mask
=
bcm2836_arm_irqchip_dummy_op
,
.
irq_unmask
=
bcm2836_arm_irqchip_dummy_op
,
.
irq_eoi
=
bcm2836_arm_irqchip_ipi_eoi
,
.
ipi_send_mask
=
bcm2836_arm_irqchip_ipi_send_mask
,
};
static
int
bcm2836_arm_irqchip_ipi_alloc
(
struct
irq_domain
*
d
,
unsigned
int
virq
,
unsigned
int
nr_irqs
,
void
*
args
)
{
int
i
;
for
(
i
=
0
;
i
<
nr_irqs
;
i
++
)
{
irq_set_percpu_devid
(
virq
+
i
);
irq_domain_set_info
(
d
,
virq
+
i
,
i
,
&
bcm2836_arm_irqchip_ipi
,
d
->
host_data
,
handle_percpu_devid_fasteoi_ipi
,
NULL
,
NULL
);
}
return
0
;
}
static
void
bcm2836_arm_irqchip_ipi_free
(
struct
irq_domain
*
d
,
unsigned
int
virq
,
unsigned
int
nr_irqs
)
{
/* Not freeing IPIs */
}
static
const
struct
irq_domain_ops
ipi_domain_ops
=
{
.
alloc
=
bcm2836_arm_irqchip_ipi_alloc
,
.
free
=
bcm2836_arm_irqchip_ipi_free
,
};
static
int
bcm2836_cpu_starting
(
unsigned
int
cpu
)
{
bcm2836_arm_irqchip_unmask_per_cpu_irq
(
LOCAL_MAILBOX_INT_CONTROL0
,
0
,
...
...
@@ -175,25 +241,58 @@ static int bcm2836_cpu_dying(unsigned int cpu)
cpu
);
return
0
;
}
#endif
static
const
struct
irq_domain_ops
bcm2836_arm_irqchip_intc_ops
=
{
.
xlate
=
irq_domain_xlate_onetwocell
,
.
map
=
bcm2836_map
,
};
#define BITS_PER_MBOX 32
static
void
bcm2836_arm_irqchip_smp_init
(
void
)
static
void
bcm2836_arm_irqchip_smp_init
(
void
)
{
#ifdef CONFIG_SMP
struct
irq_fwspec
ipi_fwspec
=
{
.
fwnode
=
intc
.
domain
->
fwnode
,
.
param_count
=
1
,
.
param
=
{
[
0
]
=
LOCAL_IRQ_MAILBOX0
,
},
};
int
base_ipi
,
mux_irq
;
mux_irq
=
irq_create_fwspec_mapping
(
&
ipi_fwspec
);
if
(
WARN_ON
(
mux_irq
<=
0
))
return
;
ipi_domain
=
irq_domain_create_linear
(
intc
.
domain
->
fwnode
,
BITS_PER_MBOX
,
&
ipi_domain_ops
,
NULL
);
if
(
WARN_ON
(
!
ipi_domain
))
return
;
ipi_domain
->
flags
|=
IRQ_DOMAIN_FLAG_IPI_SINGLE
;
irq_domain_update_bus_token
(
ipi_domain
,
DOMAIN_BUS_IPI
);
base_ipi
=
__irq_domain_alloc_irqs
(
ipi_domain
,
-
1
,
BITS_PER_MBOX
,
NUMA_NO_NODE
,
NULL
,
false
,
NULL
);
if
(
WARN_ON
(
!
base_ipi
))
return
;
set_smp_ipi_range
(
base_ipi
,
BITS_PER_MBOX
);
irq_set_chained_handler_and_data
(
mux_irq
,
bcm2836_arm_irqchip_handle_ipi
,
NULL
);
/* Unmask IPIs to the boot CPU. */
cpuhp_setup_state
(
CPUHP_AP_IRQ_BCM2836_STARTING
,
"irqchip/bcm2836:starting"
,
bcm2836_cpu_starting
,
bcm2836_cpu_dying
);
set_smp_cross_call
(
bcm2836_arm_irqchip_send_ipi
);
#endif
}
#else
#define bcm2836_arm_irqchip_smp_init() do { } while(0)
#endif
static
const
struct
irq_domain_ops
bcm2836_arm_irqchip_intc_ops
=
{
.
xlate
=
irq_domain_xlate_onetwocell
,
.
map
=
bcm2836_map
,
};
/*
* The LOCAL_IRQ_CNT* timer firings are based off of the external
...
...
@@ -232,6 +331,8 @@ static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node,
if
(
!
intc
.
domain
)
panic
(
"%pOF: unable to create IRQ domain
\n
"
,
node
);
irq_domain_update_bus_token
(
intc
.
domain
,
DOMAIN_BUS_WIRED
);
bcm2836_arm_irqchip_smp_init
();
set_handle_irq
(
bcm2836_arm_irqchip_handle_irq
);
...
...
drivers/irqchip/irq-gic-common.c
View file @
7e62dd91
...
...
@@ -152,9 +152,6 @@ void gic_cpu_config(void __iomem *base, int nr, void (*sync_access)(void))
writel_relaxed
(
GICD_INT_DEF_PRI_X4
,
base
+
GIC_DIST_PRI
+
i
*
4
/
4
);
/* Ensure all SGI interrupts are now enabled */
writel_relaxed
(
GICD_INT_EN_SET_SGI
,
base
+
GIC_DIST_ENABLE_SET
);
if
(
sync_access
)
sync_access
();
}
drivers/irqchip/irq-gic-v3.c
View file @
7e62dd91
...
...
@@ -36,6 +36,8 @@
#define FLAGS_WORKAROUND_GICR_WAKER_MSM8996 (1ULL << 0)
#define FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539 (1ULL << 1)
#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1)
struct
redist_region
{
void
__iomem
*
redist_base
;
phys_addr_t
phys_base
;
...
...
@@ -113,6 +115,7 @@ static DEFINE_PER_CPU(bool, has_rss);
#define DEFAULT_PMR_VALUE 0xf0
enum
gic_intid_range
{
SGI_RANGE
,
PPI_RANGE
,
SPI_RANGE
,
EPPI_RANGE
,
...
...
@@ -124,6 +127,8 @@ enum gic_intid_range {
static
enum
gic_intid_range
__get_intid_range
(
irq_hw_number_t
hwirq
)
{
switch
(
hwirq
)
{
case
0
...
15
:
return
SGI_RANGE
;
case
16
...
31
:
return
PPI_RANGE
;
case
32
...
1019
:
...
...
@@ -149,15 +154,22 @@ static inline unsigned int gic_irq(struct irq_data *d)
return
d
->
hwirq
;
}
static
inline
int
gic_irq_in_rdist
(
struct
irq_data
*
d
)
static
inline
bool
gic_irq_in_rdist
(
struct
irq_data
*
d
)
{
enum
gic_intid_range
range
=
get_intid_range
(
d
);
return
range
==
PPI_RANGE
||
range
==
EPPI_RANGE
;
switch
(
get_intid_range
(
d
))
{
case
SGI_RANGE
:
case
PPI_RANGE
:
case
EPPI_RANGE
:
return
true
;
default:
return
false
;
}
}
static
inline
void
__iomem
*
gic_dist_base
(
struct
irq_data
*
d
)
{
switch
(
get_intid_range
(
d
))
{
case
SGI_RANGE
:
case
PPI_RANGE
:
case
EPPI_RANGE
:
/* SGI+PPI -> SGI_base for this CPU */
...
...
@@ -254,6 +266,7 @@ static void gic_enable_redist(bool enable)
static
u32
convert_offset_index
(
struct
irq_data
*
d
,
u32
offset
,
u32
*
index
)
{
switch
(
get_intid_range
(
d
))
{
case
SGI_RANGE
:
case
PPI_RANGE
:
case
SPI_RANGE
:
*
index
=
d
->
hwirq
;
...
...
@@ -373,7 +386,7 @@ static int gic_irq_set_irqchip_state(struct irq_data *d,
{
u32
reg
;
if
(
d
->
hwirq
>=
8192
)
/* PPI/SPI only */
if
(
d
->
hwirq
>=
8192
)
/*
SGI/
PPI/SPI only */
return
-
EINVAL
;
switch
(
which
)
{
...
...
@@ -540,12 +553,12 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
u32
offset
,
index
;
int
ret
;
/* Interrupt configuration for SGIs can't be changed */
if
(
irq
<
16
)
return
-
EINVAL
;
range
=
get_intid_range
(
d
);
/* Interrupt configuration for SGIs can't be changed */
if
(
range
==
SGI_RANGE
)
return
type
!=
IRQ_TYPE_EDGE_RISING
?
-
EINVAL
:
0
;
/* SPIs have restrictions on the supported types */
if
((
range
==
SPI_RANGE
||
range
==
ESPI_RANGE
)
&&
type
!=
IRQ_TYPE_LEVEL_HIGH
&&
type
!=
IRQ_TYPE_EDGE_RISING
)
...
...
@@ -573,6 +586,9 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
static
int
gic_irq_set_vcpu_affinity
(
struct
irq_data
*
d
,
void
*
vcpu
)
{
if
(
get_intid_range
(
d
)
==
SGI_RANGE
)
return
-
EINVAL
;
if
(
vcpu
)
irqd_set_forwarded_to_vcpu
(
d
);
else
...
...
@@ -647,38 +663,14 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
if
((
irqnr
>=
1020
&&
irqnr
<=
1023
))
return
;
/* Treat anything but SGIs in a uniform way */
if
(
likely
(
irqnr
>
15
))
{
int
err
;
if
(
static_branch_likely
(
&
supports_deactivate_key
))
gic_write_eoir
(
irqnr
);
else
isb
();
err
=
handle_domain_irq
(
gic_data
.
domain
,
irqnr
,
regs
);
if
(
err
)
{
WARN_ONCE
(
true
,
"Unexpected interrupt received!
\n
"
);
gic_deactivate_unhandled
(
irqnr
);
}
return
;
}
if
(
irqnr
<
16
)
{
if
(
static_branch_likely
(
&
supports_deactivate_key
))
gic_write_eoir
(
irqnr
);
if
(
static_branch_likely
(
&
supports_deactivate_key
))
gic_write_dir
(
irqnr
);
#ifdef CONFIG_SMP
/*
* Unlike GICv2, we don't need an smp_rmb() here.
* The control dependency from gic_read_iar to
* the ISB in gic_write_eoir is enough to ensure
* that any shared data read by handle_IPI will
* be read after the ACK.
*/
handle_IPI
(
irqnr
,
regs
);
#else
WARN_ONCE
(
true
,
"Unexpected SGI received!
\n
"
);
#endif
else
isb
();
if
(
handle_domain_irq
(
gic_data
.
domain
,
irqnr
,
regs
))
{
WARN_ONCE
(
true
,
"Unexpected interrupt received!
\n
"
);
gic_deactivate_unhandled
(
irqnr
);
}
}
...
...
@@ -1132,11 +1124,11 @@ static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq)
gic_write_sgi1r
(
val
);
}
static
void
gic_
raise_softirq
(
const
struct
cpumask
*
mask
,
unsigned
int
irq
)
static
void
gic_
ipi_send_mask
(
struct
irq_data
*
d
,
const
struct
cpumask
*
mask
)
{
int
cpu
;
if
(
WARN_ON
(
irq
>=
16
))
if
(
WARN_ON
(
d
->
hw
irq
>=
16
))
return
;
/*
...
...
@@ -1150,7 +1142,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
u16
tlist
;
tlist
=
gic_compute_target_list
(
&
cpu
,
mask
,
cluster_id
);
gic_send_sgi
(
cluster_id
,
tlist
,
irq
);
gic_send_sgi
(
cluster_id
,
tlist
,
d
->
hw
irq
);
}
/* Force the above writes to ICC_SGI1R_EL1 to be executed */
...
...
@@ -1159,10 +1151,24 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
static
void
__init
gic_smp_init
(
void
)
{
set_smp_cross_call
(
gic_raise_softirq
);
struct
irq_fwspec
sgi_fwspec
=
{
.
fwnode
=
gic_data
.
fwnode
,
.
param_count
=
1
,
};
int
base_sgi
;
cpuhp_setup_state_nocalls
(
CPUHP_AP_IRQ_GIC_STARTING
,
"irqchip/arm/gicv3:starting"
,
gic_starting_cpu
,
NULL
);
/* Register all 8 non-secure SGIs */
base_sgi
=
__irq_domain_alloc_irqs
(
gic_data
.
domain
,
-
1
,
8
,
NUMA_NO_NODE
,
&
sgi_fwspec
,
false
,
NULL
);
if
(
WARN_ON
(
base_sgi
<=
0
))
return
;
set_smp_ipi_range
(
base_sgi
,
8
);
}
static
int
gic_set_affinity
(
struct
irq_data
*
d
,
const
struct
cpumask
*
mask_val
,
...
...
@@ -1211,6 +1217,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
}
#else
#define gic_set_affinity NULL
#define gic_ipi_send_mask NULL
#define gic_smp_init() do { } while(0)
#endif
...
...
@@ -1253,6 +1260,7 @@ static struct irq_chip gic_chip = {
.
irq_set_irqchip_state
=
gic_irq_set_irqchip_state
,
.
irq_nmi_setup
=
gic_irq_nmi_setup
,
.
irq_nmi_teardown
=
gic_irq_nmi_teardown
,
.
ipi_send_mask
=
gic_ipi_send_mask
,
.
flags
=
IRQCHIP_SET_TYPE_MASKED
|
IRQCHIP_SKIP_SET_WAKE
|
IRQCHIP_MASK_ON_SUSPEND
,
...
...
@@ -1270,6 +1278,7 @@ static struct irq_chip gic_eoimode1_chip = {
.
irq_set_vcpu_affinity
=
gic_irq_set_vcpu_affinity
,
.
irq_nmi_setup
=
gic_irq_nmi_setup
,
.
irq_nmi_teardown
=
gic_irq_nmi_teardown
,
.
ipi_send_mask
=
gic_ipi_send_mask
,
.
flags
=
IRQCHIP_SET_TYPE_MASKED
|
IRQCHIP_SKIP_SET_WAKE
|
IRQCHIP_MASK_ON_SUSPEND
,
...
...
@@ -1284,6 +1293,13 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
chip
=
&
gic_eoimode1_chip
;
switch
(
__get_intid_range
(
hw
))
{
case
SGI_RANGE
:
irq_set_percpu_devid
(
irq
);
irq_domain_set_info
(
d
,
irq
,
hw
,
chip
,
d
->
host_data
,
handle_percpu_devid_fasteoi_ipi
,
NULL
,
NULL
);
break
;
case
PPI_RANGE
:
case
EPPI_RANGE
:
irq_set_percpu_devid
(
irq
);
...
...
@@ -1313,13 +1329,17 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
return
0
;
}
#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1)
static
int
gic_irq_domain_translate
(
struct
irq_domain
*
d
,
struct
irq_fwspec
*
fwspec
,
unsigned
long
*
hwirq
,
unsigned
int
*
type
)
{
if
(
fwspec
->
param_count
==
1
&&
fwspec
->
param
[
0
]
<
16
)
{
*
hwirq
=
fwspec
->
param
[
0
];
*
type
=
IRQ_TYPE_EDGE_RISING
;
return
0
;
}
if
(
is_of_node
(
fwspec
->
fwnode
))
{
if
(
fwspec
->
param_count
<
3
)
return
-
EINVAL
;
...
...
@@ -1676,9 +1696,9 @@ static int __init gic_init_bases(void __iomem *dist_base,
gic_update_rdist_properties
();
gic_smp_init
();
gic_dist_init
();
gic_cpu_init
();
gic_smp_init
();
gic_cpu_pm_init
();
if
(
gic_dist_supports_lpis
())
{
...
...
drivers/irqchip/irq-gic.c
View file @
7e62dd91
...
...
@@ -83,9 +83,6 @@ struct gic_chip_data {
#endif
struct
irq_domain
*
domain
;
unsigned
int
gic_irqs
;
#ifdef CONFIG_GIC_NON_BANKED
void
__iomem
*
(
*
get_base
)(
union
gic_base
*
);
#endif
};
#ifdef CONFIG_BL_SWITCHER
...
...
@@ -124,36 +121,30 @@ static struct gic_chip_data gic_data[CONFIG_ARM_GIC_MAX_NR] __read_mostly;
static
struct
gic_kvm_info
gic_v2_kvm_info
;
static
DEFINE_PER_CPU
(
u32
,
sgi_intid
);
#ifdef CONFIG_GIC_NON_BANKED
static
void
__iomem
*
gic_get_percpu_base
(
union
gic_base
*
base
)
{
return
raw_cpu_read
(
*
base
->
percpu_base
);
}
static
DEFINE_STATIC_KEY_FALSE
(
frankengic_key
);
static
void
__iomem
*
gic_get_common_base
(
union
gic_base
*
base
)
static
void
enable_frankengic
(
void
)
{
return
base
->
common_base
;
static_branch_enable
(
&
frankengic_key
)
;
}
static
inline
void
__iomem
*
gic_data_dist_base
(
struct
gic_chip_data
*
data
)
static
inline
void
__iomem
*
__get_base
(
union
gic_base
*
base
)
{
return
data
->
get_base
(
&
data
->
dist_base
);
}
if
(
static_branch_unlikely
(
&
frankengic_key
))
return
raw_cpu_read
(
*
base
->
percpu_base
);
static
inline
void
__iomem
*
gic_data_cpu_base
(
struct
gic_chip_data
*
data
)
{
return
data
->
get_base
(
&
data
->
cpu_base
);
return
base
->
common_base
;
}
static
inline
void
gic_set_base_accessor
(
struct
gic_chip_data
*
data
,
void
__iomem
*
(
*
f
)(
union
gic_base
*
))
{
data
->
get_base
=
f
;
}
#define gic_data_dist_base(d) __get_base(&(d)->dist_base)
#define gic_data_cpu_base(d) __get_base(&(d)->cpu_base)
#else
#define gic_data_dist_base(d) ((d)->dist_base.common_base)
#define gic_data_cpu_base(d) ((d)->cpu_base.common_base)
#define
gic_set_base_accessor(d, f
)
#define
enable_frankengic() do { } while(0
)
#endif
static
inline
void
__iomem
*
gic_dist_base
(
struct
irq_data
*
d
)
...
...
@@ -226,16 +217,26 @@ static void gic_unmask_irq(struct irq_data *d)
static
void
gic_eoi_irq
(
struct
irq_data
*
d
)
{
writel_relaxed
(
gic_irq
(
d
),
gic_cpu_base
(
d
)
+
GIC_CPU_EOI
);
u32
hwirq
=
gic_irq
(
d
);
if
(
hwirq
<
16
)
hwirq
=
this_cpu_read
(
sgi_intid
);
writel_relaxed
(
hwirq
,
gic_cpu_base
(
d
)
+
GIC_CPU_EOI
);
}
static
void
gic_eoimode1_eoi_irq
(
struct
irq_data
*
d
)
{
u32
hwirq
=
gic_irq
(
d
);
/* Do not deactivate an IRQ forwarded to a vcpu. */
if
(
irqd_is_forwarded_to_vcpu
(
d
))
return
;
writel_relaxed
(
gic_irq
(
d
),
gic_cpu_base
(
d
)
+
GIC_CPU_DEACTIVATE
);
if
(
hwirq
<
16
)
hwirq
=
this_cpu_read
(
sgi_intid
);
writel_relaxed
(
hwirq
,
gic_cpu_base
(
d
)
+
GIC_CPU_DEACTIVATE
);
}
static
int
gic_irq_set_irqchip_state
(
struct
irq_data
*
d
,
...
...
@@ -295,7 +296,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
/* Interrupt configuration for SGIs can't be changed */
if
(
gicirq
<
16
)
return
-
EINVAL
;
return
type
!=
IRQ_TYPE_EDGE_RISING
?
-
EINVAL
:
0
;
/* SPIs have restrictions on the supported types */
if
(
gicirq
>=
32
&&
type
!=
IRQ_TYPE_LEVEL_HIGH
&&
...
...
@@ -315,7 +316,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type)
static
int
gic_irq_set_vcpu_affinity
(
struct
irq_data
*
d
,
void
*
vcpu
)
{
/* Only interrupts on the primary GIC can be forwarded to a vcpu. */
if
(
cascading_gic_irq
(
d
))
if
(
cascading_gic_irq
(
d
)
||
gic_irq
(
d
)
<
16
)
return
-
EINVAL
;
if
(
vcpu
)
...
...
@@ -325,28 +326,6 @@ static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
return
0
;
}
#ifdef CONFIG_SMP
static
int
gic_set_affinity
(
struct
irq_data
*
d
,
const
struct
cpumask
*
mask_val
,
bool
force
)
{
void
__iomem
*
reg
=
gic_dist_base
(
d
)
+
GIC_DIST_TARGET
+
gic_irq
(
d
);
unsigned
int
cpu
;
if
(
!
force
)
cpu
=
cpumask_any_and
(
mask_val
,
cpu_online_mask
);
else
cpu
=
cpumask_first
(
mask_val
);
if
(
cpu
>=
NR_GIC_CPU_IF
||
cpu
>=
nr_cpu_ids
)
return
-
EINVAL
;
writeb_relaxed
(
gic_cpu_map
[
cpu
],
reg
);
irq_data_update_effective_affinity
(
d
,
cpumask_of
(
cpu
));
return
IRQ_SET_MASK_OK_DONE
;
}
#endif
static
void
__exception_irq_entry
gic_handle_irq
(
struct
pt_regs
*
regs
)
{
u32
irqstat
,
irqnr
;
...
...
@@ -357,31 +336,33 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
irqstat
=
readl_relaxed
(
cpu_base
+
GIC_CPU_INTACK
);
irqnr
=
irqstat
&
GICC_IAR_INT_ID_MASK
;
if
(
likely
(
irqnr
>
15
&&
irqnr
<
1020
))
{
if
(
static_branch_likely
(
&
supports_deactivate_key
))
writel_relaxed
(
irqstat
,
cpu_base
+
GIC_CPU_EOI
);
isb
();
handle_domain_irq
(
gic
->
domain
,
irqnr
,
regs
);
continue
;
}
if
(
irqnr
<
16
)
{
if
(
unlikely
(
irqnr
>=
1020
))
break
;
if
(
static_branch_likely
(
&
supports_deactivate_key
))
writel_relaxed
(
irqstat
,
cpu_base
+
GIC_CPU_EOI
);
if
(
static_branch_likely
(
&
supports_deactivate_key
))
writel_relaxed
(
irqstat
,
cpu_base
+
GIC_CPU_DEACTIVATE
);
#ifdef CONFIG_SMP
isb
();
/*
* Ensure any shared data written by the CPU sending the IPI
* is read after we've read the ACK register on the GIC.
*
* Pairs with the write barrier in gic_ipi_send_mask
*/
if
(
irqnr
<=
15
)
{
smp_rmb
();
/*
*
Ensure any shared data written by the CPU sending
*
the IPI is read after we've read the ACK register
*
on the GIC.
*
*
Pairs with the write barrier in gic_raise_softirq
*
The GIC encodes the source CPU in GICC_IAR,
*
leading to the deactivation to fail if not
*
written back as is to GICC_EOI. Stash the INTID
*
away for gic_eoi_irq() to write back. This only
*
works because we don't nest SGIs...
*/
smp_rmb
();
handle_IPI
(
irqnr
,
regs
);
#endif
continue
;
this_cpu_write
(
sgi_intid
,
irqstat
);
}
break
;
handle_domain_irq
(
gic
->
domain
,
irqnr
,
regs
);
}
while
(
1
);
}
...
...
@@ -728,11 +709,6 @@ static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v)
int
i
;
for
(
i
=
0
;
i
<
CONFIG_ARM_GIC_MAX_NR
;
i
++
)
{
#ifdef CONFIG_GIC_NON_BANKED
/* Skip over unused GICs */
if
(
!
gic_data
[
i
].
get_base
)
continue
;
#endif
switch
(
cmd
)
{
case
CPU_PM_ENTER
:
gic_cpu_save
(
&
gic_data
[
i
]);
...
...
@@ -795,14 +771,34 @@ static int gic_pm_init(struct gic_chip_data *gic)
#endif
#ifdef CONFIG_SMP
static
void
gic_raise_softirq
(
const
struct
cpumask
*
mask
,
unsigned
int
irq
)
static
int
gic_set_affinity
(
struct
irq_data
*
d
,
const
struct
cpumask
*
mask_val
,
bool
force
)
{
void
__iomem
*
reg
=
gic_dist_base
(
d
)
+
GIC_DIST_TARGET
+
gic_irq
(
d
);
unsigned
int
cpu
;
if
(
!
force
)
cpu
=
cpumask_any_and
(
mask_val
,
cpu_online_mask
);
else
cpu
=
cpumask_first
(
mask_val
);
if
(
cpu
>=
NR_GIC_CPU_IF
||
cpu
>=
nr_cpu_ids
)
return
-
EINVAL
;
writeb_relaxed
(
gic_cpu_map
[
cpu
],
reg
);
irq_data_update_effective_affinity
(
d
,
cpumask_of
(
cpu
));
return
IRQ_SET_MASK_OK_DONE
;
}
static
void
gic_ipi_send_mask
(
struct
irq_data
*
d
,
const
struct
cpumask
*
mask
)
{
int
cpu
;
unsigned
long
flags
,
map
=
0
;
if
(
unlikely
(
nr_cpu_ids
==
1
))
{
/* Only one CPU? let's do a self-IPI... */
writel_relaxed
(
2
<<
24
|
irq
,
writel_relaxed
(
2
<<
24
|
d
->
hw
irq
,
gic_data_dist_base
(
&
gic_data
[
0
])
+
GIC_DIST_SOFTINT
);
return
;
}
...
...
@@ -820,10 +816,41 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
dmb
(
ishst
);
/* this always happens on GIC0 */
writel_relaxed
(
map
<<
16
|
irq
,
gic_data_dist_base
(
&
gic_data
[
0
])
+
GIC_DIST_SOFTINT
);
writel_relaxed
(
map
<<
16
|
d
->
hw
irq
,
gic_data_dist_base
(
&
gic_data
[
0
])
+
GIC_DIST_SOFTINT
);
gic_unlock_irqrestore
(
flags
);
}
static
int
gic_starting_cpu
(
unsigned
int
cpu
)
{
gic_cpu_init
(
&
gic_data
[
0
]);
return
0
;
}
static
__init
void
gic_smp_init
(
void
)
{
struct
irq_fwspec
sgi_fwspec
=
{
.
fwnode
=
gic_data
[
0
].
domain
->
fwnode
,
.
param_count
=
1
,
};
int
base_sgi
;
cpuhp_setup_state_nocalls
(
CPUHP_AP_IRQ_GIC_STARTING
,
"irqchip/arm/gic:starting"
,
gic_starting_cpu
,
NULL
);
base_sgi
=
__irq_domain_alloc_irqs
(
gic_data
[
0
].
domain
,
-
1
,
8
,
NUMA_NO_NODE
,
&
sgi_fwspec
,
false
,
NULL
);
if
(
WARN_ON
(
base_sgi
<=
0
))
return
;
set_smp_ipi_range
(
base_sgi
,
8
);
}
#else
#define gic_smp_init() do { } while(0)
#define gic_set_affinity NULL
#define gic_ipi_send_mask NULL
#endif
#ifdef CONFIG_BL_SWITCHER
...
...
@@ -970,15 +997,24 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
{
struct
gic_chip_data
*
gic
=
d
->
host_data
;
if
(
hw
<
32
)
{
switch
(
hw
)
{
case
0
...
15
:
irq_set_percpu_devid
(
irq
);
irq_domain_set_info
(
d
,
irq
,
hw
,
&
gic
->
chip
,
d
->
host_data
,
handle_percpu_devid_fasteoi_ipi
,
NULL
,
NULL
);
break
;
case
16
...
31
:
irq_set_percpu_devid
(
irq
);
irq_domain_set_info
(
d
,
irq
,
hw
,
&
gic
->
chip
,
d
->
host_data
,
handle_percpu_devid_irq
,
NULL
,
NULL
);
}
else
{
break
;
default:
irq_domain_set_info
(
d
,
irq
,
hw
,
&
gic
->
chip
,
d
->
host_data
,
handle_fasteoi_irq
,
NULL
,
NULL
);
irq_set_probe
(
irq
);
irqd_set_single_target
(
irq_desc_get_irq_data
(
irq_to_desc
(
irq
)));
break
;
}
return
0
;
}
...
...
@@ -992,19 +1028,26 @@ static int gic_irq_domain_translate(struct irq_domain *d,
unsigned
long
*
hwirq
,
unsigned
int
*
type
)
{
if
(
fwspec
->
param_count
==
1
&&
fwspec
->
param
[
0
]
<
16
)
{
*
hwirq
=
fwspec
->
param
[
0
];
*
type
=
IRQ_TYPE_EDGE_RISING
;
return
0
;
}
if
(
is_of_node
(
fwspec
->
fwnode
))
{
if
(
fwspec
->
param_count
<
3
)
return
-
EINVAL
;
/* Get the interrupt number and add 16 to skip over SGIs */
*
hwirq
=
fwspec
->
param
[
1
]
+
16
;
/*
* For SPIs, we need to add 16 more to get the GIC irq
* ID number
*/
if
(
!
fwspec
->
param
[
0
])
*
hwirq
+=
16
;
switch
(
fwspec
->
param
[
0
])
{
case
0
:
/* SPI */
*
hwirq
=
fwspec
->
param
[
1
]
+
32
;
break
;
case
1
:
/* PPI */
*
hwirq
=
fwspec
->
param
[
1
]
+
16
;
break
;
default:
return
-
EINVAL
;
}
*
type
=
fwspec
->
param
[
2
]
&
IRQ_TYPE_SENSE_MASK
;
...
...
@@ -1027,12 +1070,6 @@ static int gic_irq_domain_translate(struct irq_domain *d,
return
-
EINVAL
;
}
static
int
gic_starting_cpu
(
unsigned
int
cpu
)
{
gic_cpu_init
(
&
gic_data
[
0
]);
return
0
;
}
static
int
gic_irq_domain_alloc
(
struct
irq_domain
*
domain
,
unsigned
int
virq
,
unsigned
int
nr_irqs
,
void
*
arg
)
{
...
...
@@ -1079,10 +1116,10 @@ static void gic_init_chip(struct gic_chip_data *gic, struct device *dev,
gic
->
chip
.
irq_set_vcpu_affinity
=
gic_irq_set_vcpu_affinity
;
}
#ifdef CONFIG_SMP
if
(
gic
==
&
gic_data
[
0
])
if
(
gic
==
&
gic_data
[
0
])
{
gic
->
chip
.
irq_set_affinity
=
gic_set_affinity
;
#endif
gic
->
chip
.
ipi_send_mask
=
gic_ipi_send_mask
;
}
}
static
int
gic_init_bases
(
struct
gic_chip_data
*
gic
,
...
...
@@ -1112,7 +1149,7 @@ static int gic_init_bases(struct gic_chip_data *gic,
gic
->
raw_cpu_base
+
offset
;
}
gic_set_base_accessor
(
gic
,
gic_get_percpu_base
);
enable_frankengic
(
);
}
else
{
/* Normal, sane GIC... */
WARN
(
gic
->
percpu_offset
,
...
...
@@ -1120,7 +1157,6 @@ static int gic_init_bases(struct gic_chip_data *gic,
gic
->
percpu_offset
);
gic
->
dist_base
.
common_base
=
gic
->
raw_dist_base
;
gic
->
cpu_base
.
common_base
=
gic
->
raw_cpu_base
;
gic_set_base_accessor
(
gic
,
gic_get_common_base
);
}
/*
...
...
@@ -1199,12 +1235,7 @@ static int __init __gic_init_bases(struct gic_chip_data *gic,
*/
for
(
i
=
0
;
i
<
NR_GIC_CPU_IF
;
i
++
)
gic_cpu_map
[
i
]
=
0xff
;
#ifdef CONFIG_SMP
set_smp_cross_call
(
gic_raise_softirq
);
#endif
cpuhp_setup_state_nocalls
(
CPUHP_AP_IRQ_GIC_STARTING
,
"irqchip/arm/gic:starting"
,
gic_starting_cpu
,
NULL
);
set_handle_irq
(
gic_handle_irq
);
if
(
static_branch_likely
(
&
supports_deactivate_key
))
pr_info
(
"GIC: Using split EOI/Deactivate mode
\n
"
);
...
...
@@ -1221,6 +1252,8 @@ static int __init __gic_init_bases(struct gic_chip_data *gic,
ret
=
gic_init_bases
(
gic
,
handle
);
if
(
ret
)
kfree
(
name
);
else
if
(
gic
==
&
gic_data
[
0
])
gic_smp_init
();
return
ret
;
}
...
...
drivers/irqchip/irq-hip04.c
View file @
7e62dd91
...
...
@@ -171,6 +171,29 @@ static int hip04_irq_set_affinity(struct irq_data *d,
return
IRQ_SET_MASK_OK
;
}
static
void
hip04_ipi_send_mask
(
struct
irq_data
*
d
,
const
struct
cpumask
*
mask
)
{
int
cpu
;
unsigned
long
flags
,
map
=
0
;
raw_spin_lock_irqsave
(
&
irq_controller_lock
,
flags
);
/* Convert our logical CPU mask into a physical one. */
for_each_cpu
(
cpu
,
mask
)
map
|=
hip04_cpu_map
[
cpu
];
/*
* Ensure that stores to Normal memory are visible to the
* other CPUs before they observe us issuing the IPI.
*/
dmb
(
ishst
);
/* this always happens on GIC0 */
writel_relaxed
(
map
<<
8
|
d
->
hwirq
,
hip04_data
.
dist_base
+
GIC_DIST_SOFTINT
);
raw_spin_unlock_irqrestore
(
&
irq_controller_lock
,
flags
);
}
#endif
static
void
__exception_irq_entry
hip04_handle_irq
(
struct
pt_regs
*
regs
)
...
...
@@ -182,19 +205,9 @@ static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs)
irqstat
=
readl_relaxed
(
cpu_base
+
GIC_CPU_INTACK
);
irqnr
=
irqstat
&
GICC_IAR_INT_ID_MASK
;
if
(
likely
(
irqnr
>
15
&&
irqnr
<=
HIP04_MAX_IRQS
))
{
if
(
irqnr
<=
HIP04_MAX_IRQS
)
handle_domain_irq
(
hip04_data
.
domain
,
irqnr
,
regs
);
continue
;
}
if
(
irqnr
<
16
)
{
writel_relaxed
(
irqstat
,
cpu_base
+
GIC_CPU_EOI
);
#ifdef CONFIG_SMP
handle_IPI
(
irqnr
,
regs
);
#endif
continue
;
}
break
;
}
while
(
1
);
}
while
(
irqnr
>
HIP04_MAX_IRQS
);
}
static
struct
irq_chip
hip04_irq_chip
=
{
...
...
@@ -205,6 +218,7 @@ static struct irq_chip hip04_irq_chip = {
.
irq_set_type
=
hip04_irq_set_type
,
#ifdef CONFIG_SMP
.
irq_set_affinity
=
hip04_irq_set_affinity
,
.
ipi_send_mask
=
hip04_ipi_send_mask
,
#endif
.
flags
=
IRQCHIP_SET_TYPE_MASKED
|
IRQCHIP_SKIP_SET_WAKE
|
...
...
@@ -279,39 +293,17 @@ static void hip04_irq_cpu_init(struct hip04_irq_data *intc)
writel_relaxed
(
1
,
base
+
GIC_CPU_CTRL
);
}
#ifdef CONFIG_SMP
static
void
hip04_raise_softirq
(
const
struct
cpumask
*
mask
,
unsigned
int
irq
)
{
int
cpu
;
unsigned
long
flags
,
map
=
0
;
raw_spin_lock_irqsave
(
&
irq_controller_lock
,
flags
);
/* Convert our logical CPU mask into a physical one. */
for_each_cpu
(
cpu
,
mask
)
map
|=
hip04_cpu_map
[
cpu
];
/*
* Ensure that stores to Normal memory are visible to the
* other CPUs before they observe us issuing the IPI.
*/
dmb
(
ishst
);
/* this always happens on GIC0 */
writel_relaxed
(
map
<<
8
|
irq
,
hip04_data
.
dist_base
+
GIC_DIST_SOFTINT
);
raw_spin_unlock_irqrestore
(
&
irq_controller_lock
,
flags
);
}
#endif
static
int
hip04_irq_domain_map
(
struct
irq_domain
*
d
,
unsigned
int
irq
,
irq_hw_number_t
hw
)
{
if
(
hw
<
32
)
{
if
(
hw
<
16
)
{
irq_set_percpu_devid
(
irq
);
irq_set_chip_and_handler
(
irq
,
&
hip04_irq_chip
,
handle_percpu_devid_fasteoi_ipi
);
}
else
if
(
hw
<
32
)
{
irq_set_percpu_devid
(
irq
);
irq_set_chip_and_handler
(
irq
,
&
hip04_irq_chip
,
handle_percpu_devid_irq
);
irq_set_status_flags
(
irq
,
IRQ_NOAUTOEN
);
}
else
{
irq_set_chip_and_handler
(
irq
,
&
hip04_irq_chip
,
handle_fasteoi_irq
);
...
...
@@ -328,10 +320,13 @@ static int hip04_irq_domain_xlate(struct irq_domain *d,
unsigned
long
*
out_hwirq
,
unsigned
int
*
out_type
)
{
unsigned
long
ret
=
0
;
if
(
irq_domain_get_of_node
(
d
)
!=
controller
)
return
-
EINVAL
;
if
(
intsize
==
1
&&
intspec
[
0
]
<
16
)
{
*
out_hwirq
=
intspec
[
0
];
*
out_type
=
IRQ_TYPE_EDGE_RISING
;
return
0
;
}
if
(
intsize
<
3
)
return
-
EINVAL
;
...
...
@@ -344,7 +339,7 @@ static int hip04_irq_domain_xlate(struct irq_domain *d,
*
out_type
=
intspec
[
2
]
&
IRQ_TYPE_SENSE_MASK
;
return
ret
;
return
0
;
}
static
int
hip04_irq_starting_cpu
(
unsigned
int
cpu
)
...
...
@@ -361,7 +356,6 @@ static const struct irq_domain_ops hip04_irq_domain_ops = {
static
int
__init
hip04_of_init
(
struct
device_node
*
node
,
struct
device_node
*
parent
)
{
irq_hw_number_t
hwirq_base
=
16
;
int
nr_irqs
,
irq_base
,
i
;
if
(
WARN_ON
(
!
node
))
...
...
@@ -390,24 +384,21 @@ hip04_of_init(struct device_node *node, struct device_node *parent)
nr_irqs
=
HIP04_MAX_IRQS
;
hip04_data
.
nr_irqs
=
nr_irqs
;
nr_irqs
-=
hwirq_base
;
/* calculate # of irqs to allocate */
irq_base
=
irq_alloc_descs
(
-
1
,
hwirq_base
,
nr_irqs
,
numa_node_id
());
irq_base
=
irq_alloc_descs
(
-
1
,
0
,
nr_irqs
,
numa_node_id
());
if
(
irq_base
<
0
)
{
pr_err
(
"failed to allocate IRQ numbers
\n
"
);
return
-
EINVAL
;
}
hip04_data
.
domain
=
irq_domain_add_legacy
(
node
,
nr_irqs
,
irq_base
,
hwirq_base
,
0
,
&
hip04_irq_domain_ops
,
&
hip04_data
);
if
(
WARN_ON
(
!
hip04_data
.
domain
))
return
-
EINVAL
;
#ifdef CONFIG_SMP
set_smp_
cross_call
(
hip04_raise_softirq
);
set_smp_
ipi_range
(
irq_base
,
16
);
#endif
set_handle_irq
(
hip04_handle_irq
);
...
...
include/linux/irq.h
View file @
7e62dd91
...
...
@@ -71,6 +71,7 @@ enum irqchip_irq_state;
* it from the spurious interrupt detection
* mechanism and from core side polling.
* IRQ_DISABLE_UNLAZY - Disable lazy irq disable
* IRQ_HIDDEN - Don't show up in /proc/interrupts
*/
enum
{
IRQ_TYPE_NONE
=
0x00000000
,
...
...
@@ -97,13 +98,14 @@ enum {
IRQ_PER_CPU_DEVID
=
(
1
<<
17
),
IRQ_IS_POLLED
=
(
1
<<
18
),
IRQ_DISABLE_UNLAZY
=
(
1
<<
19
),
IRQ_HIDDEN
=
(
1
<<
20
),
};
#define IRQF_MODIFY_MASK \
(IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \
IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \
IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID | \
IRQ_IS_POLLED | IRQ_DISABLE_UNLAZY)
IRQ_IS_POLLED | IRQ_DISABLE_UNLAZY
| IRQ_HIDDEN
)
#define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING)
...
...
@@ -634,6 +636,7 @@ static inline int irq_set_parent(int irq, int parent_irq)
*/
extern
void
handle_level_irq
(
struct
irq_desc
*
desc
);
extern
void
handle_fasteoi_irq
(
struct
irq_desc
*
desc
);
extern
void
handle_percpu_devid_fasteoi_ipi
(
struct
irq_desc
*
desc
);
extern
void
handle_edge_irq
(
struct
irq_desc
*
desc
);
extern
void
handle_edge_eoi_irq
(
struct
irq_desc
*
desc
);
extern
void
handle_simple_irq
(
struct
irq_desc
*
desc
);
...
...
kernel/irq/chip.c
View file @
7e62dd91
...
...
@@ -944,6 +944,33 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
chip
->
irq_eoi
(
&
desc
->
irq_data
);
}
/**
* handle_percpu_devid_fasteoi_ipi - Per CPU local IPI handler with per cpu
* dev ids
* @desc: the interrupt description structure for this irq
*
* The biggest difference with the IRQ version is that the interrupt is
* EOIed early, as the IPI could result in a context switch, and we need to
* make sure the IPI can fire again. We also assume that the arch code has
* registered an action. If not, we are positively doomed.
*/
void
handle_percpu_devid_fasteoi_ipi
(
struct
irq_desc
*
desc
)
{
struct
irq_chip
*
chip
=
irq_desc_get_chip
(
desc
);
struct
irqaction
*
action
=
desc
->
action
;
unsigned
int
irq
=
irq_desc_get_irq
(
desc
);
irqreturn_t
res
;
__kstat_incr_irqs_this_cpu
(
desc
);
if
(
chip
->
irq_eoi
)
chip
->
irq_eoi
(
&
desc
->
irq_data
);
trace_irq_handler_entry
(
irq
,
action
);
res
=
action
->
handler
(
irq
,
raw_cpu_ptr
(
action
->
percpu_dev_id
));
trace_irq_handler_exit
(
irq
,
action
,
res
);
}
/**
* handle_percpu_devid_fasteoi_nmi - Per CPU local NMI handler with per cpu
* dev ids
...
...
kernel/irq/debugfs.c
View file @
7e62dd91
...
...
@@ -136,6 +136,7 @@ static const struct irq_bit_descr irqdesc_states[] = {
BIT_MASK_DESCR
(
_IRQ_PER_CPU_DEVID
),
BIT_MASK_DESCR
(
_IRQ_IS_POLLED
),
BIT_MASK_DESCR
(
_IRQ_DISABLE_UNLAZY
),
BIT_MASK_DESCR
(
_IRQ_HIDDEN
),
};
static
const
struct
irq_bit_descr
irqdesc_istates
[]
=
{
...
...
kernel/irq/proc.c
View file @
7e62dd91
...
...
@@ -485,7 +485,7 @@ int show_interrupts(struct seq_file *p, void *v)
rcu_read_lock
();
desc
=
irq_to_desc
(
i
);
if
(
!
desc
)
if
(
!
desc
||
irq_settings_is_hidden
(
desc
)
)
goto
outsparse
;
if
(
desc
->
kstat_irqs
)
...
...
kernel/irq/settings.h
View file @
7e62dd91
...
...
@@ -17,6 +17,7 @@ enum {
_IRQ_PER_CPU_DEVID
=
IRQ_PER_CPU_DEVID
,
_IRQ_IS_POLLED
=
IRQ_IS_POLLED
,
_IRQ_DISABLE_UNLAZY
=
IRQ_DISABLE_UNLAZY
,
_IRQ_HIDDEN
=
IRQ_HIDDEN
,
_IRQF_MODIFY_MASK
=
IRQF_MODIFY_MASK
,
};
...
...
@@ -31,6 +32,7 @@ enum {
#define IRQ_PER_CPU_DEVID GOT_YOU_MORON
#define IRQ_IS_POLLED GOT_YOU_MORON
#define IRQ_DISABLE_UNLAZY GOT_YOU_MORON
#define IRQ_HIDDEN GOT_YOU_MORON
#undef IRQF_MODIFY_MASK
#define IRQF_MODIFY_MASK GOT_YOU_MORON
...
...
@@ -167,3 +169,8 @@ static inline void irq_settings_clr_disable_unlazy(struct irq_desc *desc)
{
desc
->
status_use_accessors
&=
~
_IRQ_DISABLE_UNLAZY
;
}
static
inline
bool
irq_settings_is_hidden
(
struct
irq_desc
*
desc
)
{
return
desc
->
status_use_accessors
&
_IRQ_HIDDEN
;
}
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