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>
|
|
|
|
#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>
|
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 set_irq_flags(unsigned int irq, unsigned int iflags)
|
|
|
|
{
|
2011-02-08 05:30:49 +08:00
|
|
|
unsigned long clr = 0, set = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-25 16:46:09 +08:00
|
|
|
if (irq >= nr_irqs) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-07-02 05:30:09 +08:00
|
|
|
if (iflags & IRQF_VALID)
|
2011-02-08 05:30:49 +08:00
|
|
|
clr |= IRQ_NOREQUEST;
|
2006-07-02 05:30:09 +08:00
|
|
|
if (iflags & IRQF_PROBE)
|
2011-02-08 05:30:49 +08:00
|
|
|
clr |= IRQ_NOPROBE;
|
2006-07-02 05:30:09 +08:00
|
|
|
if (!(iflags & IRQF_NOAUTOEN))
|
2011-02-08 05:30:49 +08: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 06:20:36 +08:00
|
|
|
}
|
2012-08-16 15:49:26 +08:00
|
|
|
EXPORT_SYMBOL_GPL(set_irq_flags);
|
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)) {
|
|
|
|
outer_cache.write_sec = machine_desc->l2c_write_sec;
|
|
|
|
ret = l2x0_of_init(machine_desc->l2c_aux_val,
|
|
|
|
machine_desc->l2c_aux_mask);
|
|
|
|
if (ret)
|
|
|
|
pr_err("L2C: failed to init: %d\n", ret);
|
|
|
|
}
|
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
|
2006-07-12 05:54:34 +08:00
|
|
|
|
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);
|
2011-07-21 22:07:56 +08:00
|
|
|
const struct cpumask *affinity = d->affinity;
|
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)
|
2012-04-27 19:56:24 +08:00
|
|
|
cpumask_copy(d->affinity, 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);
|
|
|
|
|
|
|
|
if (affinity_broken && printk_ratelimit())
|
2014-09-17 03:41:43 +08:00
|
|
|
pr_warn("IRQ%u no longer affine to CPU%u\n",
|
|
|
|
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 */
|