2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* linux/arch/arm/kernel/irq.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1992 Linus Torvalds
|
|
|
|
* Modifications for ARM processor Copyright (C) 1995-2000 Russell King.
|
|
|
|
*
|
2005-06-26 02:39:45 +08:00
|
|
|
* Support for Dynamic Tick Timer Copyright (C) 2004-2005 Nokia Corporation.
|
|
|
|
* Dynamic Tick Timer written by Tony Lindgren <tony@atomide.com> and
|
|
|
|
* Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>.
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This file contains the code used by various IRQ handling routines:
|
|
|
|
* asking for different IRQ's should be done through these routines
|
|
|
|
* instead of just grabbing them. Thus setups with different IRQ numbers
|
|
|
|
* shouldn't result in any weird surprises, and installing new handlers
|
|
|
|
* should be easier.
|
|
|
|
*
|
|
|
|
* 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>
|
2006-07-02 05:30:09 +08:00
|
|
|
#include <linux/irq.h>
|
2013-03-29 04:46:44 +08:00
|
|
|
#include <linux/irqchip.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/random.h>
|
|
|
|
#include <linux/smp.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/seq_file.h>
|
2014-10-28 21:32:35 +08:00
|
|
|
#include <linux/ratelimit.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/kallsyms.h>
|
|
|
|
#include <linux/proc_fs.h>
|
2012-08-16 15:49:26 +08:00
|
|
|
#include <linux/export.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-04-28 22:24:10 +08:00
|
|
|
#include <asm/hardware/cache-l2x0.h>
|
2015-10-02 12:42:19 +08:00
|
|
|
#include <asm/hardware/cache-uniphier.h>
|
ARM: move heavy barrier support out of line
The existing memory barrier macro causes a significant amount of code
to be inserted inline at every call site. For example, in
gpio_set_irq_type(), we have this for mb():
c0344c08: f57ff04e dsb st
c0344c0c: e59f8190 ldr r8, [pc, #400] ; c0344da4 <gpio_set_irq_type+0x230>
c0344c10: e3590004 cmp r9, #4
c0344c14: e5983014 ldr r3, [r8, #20]
c0344c18: 0a000054 beq c0344d70 <gpio_set_irq_type+0x1fc>
c0344c1c: e3530000 cmp r3, #0
c0344c20: 0a000004 beq c0344c38 <gpio_set_irq_type+0xc4>
c0344c24: e50b2030 str r2, [fp, #-48] ; 0xffffffd0
c0344c28: e50bc034 str ip, [fp, #-52] ; 0xffffffcc
c0344c2c: e12fff33 blx r3
c0344c30: e51bc034 ldr ip, [fp, #-52] ; 0xffffffcc
c0344c34: e51b2030 ldr r2, [fp, #-48] ; 0xffffffd0
c0344c38: e5963004 ldr r3, [r6, #4]
Moving the outer_cache_sync() call out of line reduces the impact of
the barrier:
c0344968: f57ff04e dsb st
c034496c: e35a0004 cmp sl, #4
c0344970: e50b2030 str r2, [fp, #-48] ; 0xffffffd0
c0344974: 0a000044 beq c0344a8c <gpio_set_irq_type+0x1b8>
c0344978: ebf363dd bl c001d8f4 <arm_heavy_mb>
c034497c: e5953004 ldr r3, [r5, #4]
This should reduce the cache footprint of this code. Overall, this
results in a reduction of around 20K in the kernel size:
text data bss dec hex filename
10773970 667392 10369656 21811018 14ccf4a ../build/imx6/vmlinux-old
10754219 667392 10369656 21791267 14c8223 ../build/imx6/vmlinux-new
Another advantage to this approach is that we can finally resolve the
issue of SoCs which have their own memory barrier requirements within
multiplatform kernels (such as OMAP.) Here, the bus interconnects
need additional handling to ensure that writes become visible in the
correct order (eg, between dma_map() operations, writes to DMA
coherent memory, and MMIO accesses.)
Acked-by: Tony Lindgren <tony@atomide.com>
Acked-by: Richard Woodruff <r-woodruff2@ti.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2015-06-02 06:44:46 +08:00
|
|
|
#include <asm/outercache.h>
|
2011-10-08 18:20:42 +08:00
|
|
|
#include <asm/exception.h>
|
2010-12-20 18:18:36 +08:00
|
|
|
#include <asm/mach/arch.h>
|
2008-08-03 22:04:04 +08:00
|
|
|
#include <asm/mach/irq.h>
|
2005-06-26 02:39:45 +08:00
|
|
|
#include <asm/mach/time.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-07-02 05:30:09 +08:00
|
|
|
unsigned long irq_err_count;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-03-24 19:02:11 +08:00
|
|
|
int arch_show_interrupts(struct seq_file *p, int prec)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-08-03 22:11:29 +08:00
|
|
|
#ifdef CONFIG_FIQ
|
2011-03-24 19:02:11 +08:00
|
|
|
show_fiq_list(p, prec);
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_SMP
|
2011-03-24 19:02:11 +08:00
|
|
|
show_ipi_list(p, prec);
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
2011-03-24 19:02:11 +08:00
|
|
|
seq_printf(p, "%*s: %10lu\n", prec, "Err", irq_err_count);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
ARM: introduce handle_IRQ() not to dump exception stack
On Mon, Jul 11, 2011 at 3:52 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
...
> The __exception annotation on a function causes this to happen:
>
> [<c002406c>] (asm_do_IRQ+0x6c/0x8c) from [<c0024b84>]
> (__irq_svc+0x44/0xcc)
> Exception stack(0xc3897c78 to 0xc3897cc0)
> 7c60: 4022d320 4022e000
> 7c80: 08000075 00001000 c32273c0 c03ce1c0 c2b49b78 4022d000 c2b420b4 00000001
> 7ca0: 00000000 c3897cfc 00000000 c3897cc0 c00afc54 c002edd8 00000013 ffffffff
>
> Where that stack dump represents the pt_regs for the exception which
> happened. Any function found in while unwinding will cause this to
> be printed.
>
> If you insert a C function between the IRQ assembly and asm_do_IRQ,
> the
> dump you get from asm_do_IRQ will be the stack for your function,
> not
> the pt_regs. That makes the feature useless.
>
When __irq_svc - or any of the other exception handling assembly code -
calls the C code, the stack pointer will be pointing at the pt_regs
structure.
All the entry points into C code from the exception handling code are
marked with __exception or __exception_irq_enter to indicate that they
are one of the functions which has pt_regs above them.
Normally, when you've entered asm_do_IRQ() you will have this stack
layout (higher address towards top):
pt_regs
asm_do_IRQ frame
If you insert a C function between the exception assembly code and
asm_do_IRQ, you end up with this stack layout instead:
pt_regs
your function frame
asm_do_IRQ frame
This means when we unwind, we'll get to asm_do_IRQ, and rather than
dumping out the pt_regs, we'll dump out your functions stack frame
instead, because that's what is above the asm_do_IRQ stack frame
rather than the expected pt_regs structure.
The fix is to introduce handle_IRQ() for no exception stack dump, so
it can be called with MULTI_IRQ_HANDLER is selected and a C function
is between the assembly code and the actual IRQ handling code.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Eric Miao <eric.y.miao@gmail.com>
2011-07-12 05:25:43 +08:00
|
|
|
* handle_IRQ handles all hardware IRQ's. Decoded IRQs should
|
|
|
|
* not come via this function. Instead, they should provide their
|
|
|
|
* own 'handler'. Used by platform code implementing C-based 1st
|
|
|
|
* level decoding.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
ARM: introduce handle_IRQ() not to dump exception stack
On Mon, Jul 11, 2011 at 3:52 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
...
> The __exception annotation on a function causes this to happen:
>
> [<c002406c>] (asm_do_IRQ+0x6c/0x8c) from [<c0024b84>]
> (__irq_svc+0x44/0xcc)
> Exception stack(0xc3897c78 to 0xc3897cc0)
> 7c60: 4022d320 4022e000
> 7c80: 08000075 00001000 c32273c0 c03ce1c0 c2b49b78 4022d000 c2b420b4 00000001
> 7ca0: 00000000 c3897cfc 00000000 c3897cc0 c00afc54 c002edd8 00000013 ffffffff
>
> Where that stack dump represents the pt_regs for the exception which
> happened. Any function found in while unwinding will cause this to
> be printed.
>
> If you insert a C function between the IRQ assembly and asm_do_IRQ,
> the
> dump you get from asm_do_IRQ will be the stack for your function,
> not
> the pt_regs. That makes the feature useless.
>
When __irq_svc - or any of the other exception handling assembly code -
calls the C code, the stack pointer will be pointing at the pt_regs
structure.
All the entry points into C code from the exception handling code are
marked with __exception or __exception_irq_enter to indicate that they
are one of the functions which has pt_regs above them.
Normally, when you've entered asm_do_IRQ() you will have this stack
layout (higher address towards top):
pt_regs
asm_do_IRQ frame
If you insert a C function between the exception assembly code and
asm_do_IRQ, you end up with this stack layout instead:
pt_regs
your function frame
asm_do_IRQ frame
This means when we unwind, we'll get to asm_do_IRQ, and rather than
dumping out the pt_regs, we'll dump out your functions stack frame
instead, because that's what is above the asm_do_IRQ stack frame
rather than the expected pt_regs structure.
The fix is to introduce handle_IRQ() for no exception stack dump, so
it can be called with MULTI_IRQ_HANDLER is selected and a C function
is between the assembly code and the actual IRQ handling code.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Eric Miao <eric.y.miao@gmail.com>
2011-07-12 05:25:43 +08:00
|
|
|
void handle_IRQ(unsigned int irq, struct pt_regs *regs)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2014-08-26 18:03:18 +08:00
|
|
|
__handle_domain_irq(NULL, irq, false, regs);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
ARM: introduce handle_IRQ() not to dump exception stack
On Mon, Jul 11, 2011 at 3:52 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
...
> The __exception annotation on a function causes this to happen:
>
> [<c002406c>] (asm_do_IRQ+0x6c/0x8c) from [<c0024b84>]
> (__irq_svc+0x44/0xcc)
> Exception stack(0xc3897c78 to 0xc3897cc0)
> 7c60: 4022d320 4022e000
> 7c80: 08000075 00001000 c32273c0 c03ce1c0 c2b49b78 4022d000 c2b420b4 00000001
> 7ca0: 00000000 c3897cfc 00000000 c3897cc0 c00afc54 c002edd8 00000013 ffffffff
>
> Where that stack dump represents the pt_regs for the exception which
> happened. Any function found in while unwinding will cause this to
> be printed.
>
> If you insert a C function between the IRQ assembly and asm_do_IRQ,
> the
> dump you get from asm_do_IRQ will be the stack for your function,
> not
> the pt_regs. That makes the feature useless.
>
When __irq_svc - or any of the other exception handling assembly code -
calls the C code, the stack pointer will be pointing at the pt_regs
structure.
All the entry points into C code from the exception handling code are
marked with __exception or __exception_irq_enter to indicate that they
are one of the functions which has pt_regs above them.
Normally, when you've entered asm_do_IRQ() you will have this stack
layout (higher address towards top):
pt_regs
asm_do_IRQ frame
If you insert a C function between the exception assembly code and
asm_do_IRQ, you end up with this stack layout instead:
pt_regs
your function frame
asm_do_IRQ frame
This means when we unwind, we'll get to asm_do_IRQ, and rather than
dumping out the pt_regs, we'll dump out your functions stack frame
instead, because that's what is above the asm_do_IRQ stack frame
rather than the expected pt_regs structure.
The fix is to introduce handle_IRQ() for no exception stack dump, so
it can be called with MULTI_IRQ_HANDLER is selected and a C function
is between the assembly code and the actual IRQ handling code.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Eric Miao <eric.y.miao@gmail.com>
2011-07-12 05:25:43 +08:00
|
|
|
/*
|
|
|
|
* asm_do_IRQ is the interface to be used from assembly code.
|
|
|
|
*/
|
|
|
|
asmlinkage void __exception_irq_entry
|
|
|
|
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
handle_IRQ(irq, regs);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
void __init init_IRQ(void)
|
|
|
|
{
|
2014-04-28 22:24:10 +08:00
|
|
|
int ret;
|
|
|
|
|
2013-03-29 04:46:44 +08:00
|
|
|
if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
|
|
|
|
irqchip_init();
|
|
|
|
else
|
|
|
|
machine_desc->init_irq();
|
2014-04-28 22:24:10 +08:00
|
|
|
|
|
|
|
if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) &&
|
|
|
|
(machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) {
|
2015-01-08 14:51:45 +08:00
|
|
|
if (!outer_cache.write_sec)
|
|
|
|
outer_cache.write_sec = machine_desc->l2c_write_sec;
|
2014-04-28 22:24:10 +08:00
|
|
|
ret = l2x0_of_init(machine_desc->l2c_aux_val,
|
|
|
|
machine_desc->l2c_aux_mask);
|
2016-01-14 14:17:00 +08:00
|
|
|
if (ret && ret != -ENODEV)
|
2014-04-28 22:24:10 +08:00
|
|
|
pr_err("L2C: failed to init: %d\n", ret);
|
|
|
|
}
|
2015-10-02 12:42:19 +08:00
|
|
|
|
|
|
|
uniphier_cache_init();
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-11-21 06:00:53 +08:00
|
|
|
#ifdef CONFIG_MULTI_IRQ_HANDLER
|
|
|
|
void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
|
|
|
|
{
|
|
|
|
if (handle_arch_irq)
|
|
|
|
return;
|
|
|
|
|
|
|
|
handle_arch_irq = handle_irq;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-06-25 16:46:09 +08:00
|
|
|
#ifdef CONFIG_SPARSE_IRQ
|
|
|
|
int __init arch_probe_nr_irqs(void)
|
|
|
|
{
|
2010-12-20 18:18:36 +08:00
|
|
|
nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS;
|
2010-09-28 02:55:03 +08:00
|
|
|
return nr_irqs;
|
2010-06-25 16:46:09 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-11-03 06:24:33 +08:00
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
2011-07-21 22:14:21 +08:00
|
|
|
static bool migrate_one_irq(struct irq_desc *desc)
|
2006-07-12 05:54:34 +08:00
|
|
|
{
|
2011-07-21 22:14:21 +08:00
|
|
|
struct irq_data *d = irq_desc_get_irq_data(desc);
|
2015-06-01 16:05:25 +08:00
|
|
|
const struct cpumask *affinity = irq_data_get_affinity_mask(d);
|
2011-07-21 22:14:21 +08:00
|
|
|
struct irq_chip *c;
|
2011-01-23 20:09:36 +08:00
|
|
|
bool ret = false;
|
2006-07-12 05:54:34 +08:00
|
|
|
|
2011-07-21 22:14:21 +08:00
|
|
|
/*
|
|
|
|
* If this is a per-CPU interrupt, or the affinity does not
|
|
|
|
* include this CPU, then we have nothing to do.
|
|
|
|
*/
|
|
|
|
if (irqd_is_per_cpu(d) || !cpumask_test_cpu(smp_processor_id(), affinity))
|
|
|
|
return false;
|
|
|
|
|
2011-07-21 22:07:56 +08:00
|
|
|
if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
|
2011-07-21 22:14:21 +08:00
|
|
|
affinity = cpu_online_mask;
|
2011-01-23 20:09:36 +08:00
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
|
2011-07-21 22:14:21 +08:00
|
|
|
c = irq_data_get_irq_chip(d);
|
2012-04-27 19:56:24 +08:00
|
|
|
if (!c->irq_set_affinity)
|
2011-07-21 22:14:21 +08:00
|
|
|
pr_debug("IRQ%u: unable to set affinity\n", d->irq);
|
2014-09-02 00:14:29 +08:00
|
|
|
else if (c->irq_set_affinity(d, affinity, false) == IRQ_SET_MASK_OK && ret)
|
2015-06-01 16:05:25 +08:00
|
|
|
cpumask_copy(irq_data_get_affinity_mask(d), affinity);
|
2011-01-23 20:09:36 +08:00
|
|
|
|
|
|
|
return ret;
|
2006-07-12 05:54:34 +08:00
|
|
|
}
|
|
|
|
|
2005-11-03 06:24:33 +08:00
|
|
|
/*
|
2011-07-21 22:14:21 +08:00
|
|
|
* The current CPU has been marked offline. Migrate IRQs off this CPU.
|
|
|
|
* If the affinity settings do not allow other CPUs, force them onto any
|
2005-11-03 06:24:33 +08:00
|
|
|
* available CPU.
|
2011-07-21 22:14:21 +08:00
|
|
|
*
|
|
|
|
* Note: we must iterate over all IRQs, whether they have an attached
|
|
|
|
* action structure or not, as we need to get chained interrupts too.
|
2005-11-03 06:24:33 +08:00
|
|
|
*/
|
|
|
|
void migrate_irqs(void)
|
|
|
|
{
|
2011-07-21 22:14:21 +08:00
|
|
|
unsigned int i;
|
2010-06-25 16:46:09 +08:00
|
|
|
struct irq_desc *desc;
|
2011-01-23 20:09:36 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
local_irq_save(flags);
|
2005-11-03 06:24:33 +08:00
|
|
|
|
2010-06-25 16:46:09 +08:00
|
|
|
for_each_irq_desc(i, desc) {
|
2012-02-03 21:50:07 +08:00
|
|
|
bool affinity_broken;
|
2011-07-21 22:14:21 +08:00
|
|
|
|
2011-01-23 20:09:36 +08:00
|
|
|
raw_spin_lock(&desc->lock);
|
2011-07-21 22:14:21 +08:00
|
|
|
affinity_broken = migrate_one_irq(desc);
|
2011-01-23 20:09:36 +08:00
|
|
|
raw_spin_unlock(&desc->lock);
|
|
|
|
|
2014-10-28 21:32:35 +08:00
|
|
|
if (affinity_broken)
|
|
|
|
pr_warn_ratelimited("IRQ%u no longer affine to CPU%u\n",
|
2014-09-17 03:41:43 +08:00
|
|
|
i, smp_processor_id());
|
2005-11-03 06:24:33 +08:00
|
|
|
}
|
2011-01-23 20:09:36 +08:00
|
|
|
|
|
|
|
local_irq_restore(flags);
|
2005-11-03 06:24:33 +08:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_HOTPLUG_CPU */
|