2005-04-17 05:20:36 +07: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 01:39:45 +07: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 05:20:36 +07: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 04:30:09 +07:00
|
|
|
#include <linux/irq.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/random.h>
|
|
|
|
#include <linux/smp.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/kallsyms.h>
|
|
|
|
#include <linux/proc_fs.h>
|
2012-08-16 14:49:26 +07:00
|
|
|
#include <linux/export.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-10-08 17:20:42 +07:00
|
|
|
#include <asm/exception.h>
|
2010-12-20 17:18:36 +07:00
|
|
|
#include <asm/mach/arch.h>
|
2008-08-03 21:04:04 +07:00
|
|
|
#include <asm/mach/irq.h>
|
2005-06-26 01:39:45 +07:00
|
|
|
#include <asm/mach/time.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-07-02 04:30:09 +07:00
|
|
|
unsigned long irq_err_count;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2011-03-24 18:02:11 +07:00
|
|
|
int arch_show_interrupts(struct seq_file *p, int prec)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2009-08-03 21:11:29 +07:00
|
|
|
#ifdef CONFIG_FIQ
|
2011-03-24 18:02:11 +07:00
|
|
|
show_fiq_list(p, prec);
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_SMP
|
2011-03-24 18:02:11 +07:00
|
|
|
show_ipi_list(p, prec);
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif
|
2011-03-24 18:02:11 +07:00
|
|
|
seq_printf(p, "%*s: %10lu\n", prec, "Err", irq_err_count);
|
2005-04-17 05:20:36 +07: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 04:25:43 +07: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 05:20:36 +07: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 04:25:43 +07:00
|
|
|
void handle_IRQ(unsigned int irq, struct pt_regs *regs)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-10-07 03:11:15 +07:00
|
|
|
struct pt_regs *old_regs = set_irq_regs(regs);
|
2008-10-09 19:36:24 +07:00
|
|
|
|
|
|
|
irq_enter();
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Some hardware gives randomly wrong interrupts. Rather
|
|
|
|
* than crashing, do something sensible.
|
|
|
|
*/
|
2010-06-25 15:46:09 +07:00
|
|
|
if (unlikely(irq >= nr_irqs)) {
|
2009-06-22 15:23:36 +07:00
|
|
|
if (printk_ratelimit())
|
|
|
|
printk(KERN_WARNING "Bad IRQ%u\n", irq);
|
|
|
|
ack_bad_irq(irq);
|
|
|
|
} else {
|
2008-10-09 19:36:24 +07:00
|
|
|
generic_handle_irq(irq);
|
2009-06-22 15:23:36 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
irq_exit();
|
2006-10-07 03:11:15 +07:00
|
|
|
set_irq_regs(old_regs);
|
2005-04-17 05:20:36 +07: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 04:25:43 +07: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 05:20:36 +07:00
|
|
|
void set_irq_flags(unsigned int irq, unsigned int iflags)
|
|
|
|
{
|
2011-02-08 04:30:49 +07:00
|
|
|
unsigned long clr = 0, set = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-06-25 15:46:09 +07:00
|
|
|
if (irq >= nr_irqs) {
|
2005-04-17 05:20:36 +07:00
|
|
|
printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-07-02 04:30:09 +07:00
|
|
|
if (iflags & IRQF_VALID)
|
2011-02-08 04:30:49 +07:00
|
|
|
clr |= IRQ_NOREQUEST;
|
2006-07-02 04:30:09 +07:00
|
|
|
if (iflags & IRQF_PROBE)
|
2011-02-08 04:30:49 +07:00
|
|
|
clr |= IRQ_NOPROBE;
|
2006-07-02 04:30:09 +07:00
|
|
|
if (!(iflags & IRQF_NOAUTOEN))
|
2011-02-08 04:30:49 +07:00
|
|
|
clr |= IRQ_NOAUTOEN;
|
|
|
|
/* Order is clear bits in "clr" then set bits in "set" */
|
|
|
|
irq_modify_status(irq, clr, set & ~clr);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2012-08-16 14:49:26 +07:00
|
|
|
EXPORT_SYMBOL_GPL(set_irq_flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
void __init init_IRQ(void)
|
|
|
|
{
|
2010-12-20 17:18:36 +07:00
|
|
|
machine_desc->init_irq();
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2012-11-21 05:00:53 +07: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 15:46:09 +07:00
|
|
|
#ifdef CONFIG_SPARSE_IRQ
|
|
|
|
int __init arch_probe_nr_irqs(void)
|
|
|
|
{
|
2010-12-20 17:18:36 +07:00
|
|
|
nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS;
|
2010-09-28 01:55:03 +07:00
|
|
|
return nr_irqs;
|
2010-06-25 15:46:09 +07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-11-03 05:24:33 +07:00
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
2006-07-12 04:54:34 +07:00
|
|
|
|
2011-07-21 21:14:21 +07:00
|
|
|
static bool migrate_one_irq(struct irq_desc *desc)
|
2006-07-12 04:54:34 +07:00
|
|
|
{
|
2011-07-21 21:14:21 +07:00
|
|
|
struct irq_data *d = irq_desc_get_irq_data(desc);
|
2011-07-21 21:07:56 +07:00
|
|
|
const struct cpumask *affinity = d->affinity;
|
2011-07-21 21:14:21 +07:00
|
|
|
struct irq_chip *c;
|
2011-01-23 19:09:36 +07:00
|
|
|
bool ret = false;
|
2006-07-12 04:54:34 +07:00
|
|
|
|
2011-07-21 21:14:21 +07: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 21:07:56 +07:00
|
|
|
if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
|
2011-07-21 21:14:21 +07:00
|
|
|
affinity = cpu_online_mask;
|
2011-01-23 19:09:36 +07:00
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
|
2011-07-21 21:14:21 +07:00
|
|
|
c = irq_data_get_irq_chip(d);
|
2012-04-27 18:56:24 +07:00
|
|
|
if (!c->irq_set_affinity)
|
2011-07-21 21:14:21 +07:00
|
|
|
pr_debug("IRQ%u: unable to set affinity\n", d->irq);
|
2012-04-27 18:56:24 +07:00
|
|
|
else if (c->irq_set_affinity(d, affinity, true) == IRQ_SET_MASK_OK && ret)
|
|
|
|
cpumask_copy(d->affinity, affinity);
|
2011-01-23 19:09:36 +07:00
|
|
|
|
|
|
|
return ret;
|
2006-07-12 04:54:34 +07:00
|
|
|
}
|
|
|
|
|
2005-11-03 05:24:33 +07:00
|
|
|
/*
|
2011-07-21 21:14:21 +07: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 05:24:33 +07:00
|
|
|
* available CPU.
|
2011-07-21 21:14:21 +07: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 05:24:33 +07:00
|
|
|
*/
|
|
|
|
void migrate_irqs(void)
|
|
|
|
{
|
2011-07-21 21:14:21 +07:00
|
|
|
unsigned int i;
|
2010-06-25 15:46:09 +07:00
|
|
|
struct irq_desc *desc;
|
2011-01-23 19:09:36 +07:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
local_irq_save(flags);
|
2005-11-03 05:24:33 +07:00
|
|
|
|
2010-06-25 15:46:09 +07:00
|
|
|
for_each_irq_desc(i, desc) {
|
2012-02-03 20:50:07 +07:00
|
|
|
bool affinity_broken;
|
2011-07-21 21:14:21 +07:00
|
|
|
|
2011-01-23 19:09:36 +07:00
|
|
|
raw_spin_lock(&desc->lock);
|
2011-07-21 21:14:21 +07:00
|
|
|
affinity_broken = migrate_one_irq(desc);
|
2011-01-23 19:09:36 +07:00
|
|
|
raw_spin_unlock(&desc->lock);
|
|
|
|
|
|
|
|
if (affinity_broken && printk_ratelimit())
|
2011-07-21 21:14:21 +07:00
|
|
|
pr_warning("IRQ%u no longer affine to CPU%u\n", i,
|
|
|
|
smp_processor_id());
|
2005-11-03 05:24:33 +07:00
|
|
|
}
|
2011-01-23 19:09:36 +07:00
|
|
|
|
|
|
|
local_irq_restore(flags);
|
2005-11-03 05:24:33 +07:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_HOTPLUG_CPU */
|