summaryrefslogtreecommitdiffstats
path: root/arch/mn10300/kernel/irq.c
diff options
context:
space:
mode:
authorAkira Takeuchi <takeuchi.akr@jp.panasonic.com>2010-10-27 17:28:55 +0100
committerDavid Howells <dhowells@redhat.com>2010-10-27 17:28:55 +0100
commit368dd5acd154b09c043cc4392a74da01599b37d5 (patch)
treedd94ae3d044f6e774dec2437613515bd6b46dacb /arch/mn10300/kernel/irq.c
parent04157a6e7df99fd5ed64955233d6e00ab6613614 (diff)
downloadblackbird-op-linux-368dd5acd154b09c043cc4392a74da01599b37d5.tar.gz
blackbird-op-linux-368dd5acd154b09c043cc4392a74da01599b37d5.zip
MN10300: And Panasonic AM34 subarch and implement SMP
Implement the Panasonic MN10300 AM34 CPU subarch and implement SMP support for MN10300. Also implement support for the MN2WS0060 processor and the ASB2364 evaluation board which are AM34 based. Signed-off-by: Akira Takeuchi <takeuchi.akr@jp.panasonic.com> Signed-off-by: Kiyoshi Owada <owada.kiyoshi@jp.panasonic.com> Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'arch/mn10300/kernel/irq.c')
-rw-r--r--arch/mn10300/kernel/irq.c266
1 files changed, 241 insertions, 25 deletions
diff --git a/arch/mn10300/kernel/irq.c b/arch/mn10300/kernel/irq.c
index b5b970d2954a..80f15725ecad 100644
--- a/arch/mn10300/kernel/irq.c
+++ b/arch/mn10300/kernel/irq.c
@@ -12,11 +12,34 @@
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/seq_file.h>
+#include <linux/cpumask.h>
#include <asm/setup.h>
+#include <asm/serial-regs.h>
-unsigned long __mn10300_irq_enabled_epsw = EPSW_IE | EPSW_IM_7;
+#ifdef CONFIG_SMP
+#undef GxICR
+#define GxICR(X) CROSS_GxICR(X, irq_affinity_online[X])
+
+#undef GxICR_u8
+#define GxICR_u8(X) CROSS_GxICR_u8(X, irq_affinity_online[X])
+#endif /* CONFIG_SMP */
+
+unsigned long __mn10300_irq_enabled_epsw[NR_CPUS] __cacheline_aligned_in_smp = {
+ [0 ... NR_CPUS - 1] = EPSW_IE | EPSW_IM_7
+};
EXPORT_SYMBOL(__mn10300_irq_enabled_epsw);
+#ifdef CONFIG_SMP
+static char irq_affinity_online[NR_IRQS] = {
+ [0 ... NR_IRQS - 1] = 0
+};
+
+#define NR_IRQ_WORDS ((NR_IRQS + 31) / 32)
+static unsigned long irq_affinity_request[NR_IRQ_WORDS] = {
+ [0 ... NR_IRQ_WORDS - 1] = 0
+};
+#endif /* CONFIG_SMP */
+
atomic_t irq_err_count;
/*
@@ -24,30 +47,65 @@ atomic_t irq_err_count;
*/
static void mn10300_cpupic_ack(unsigned int irq)
{
+ unsigned long flags;
+ u16 tmp;
+
+ flags = arch_local_cli_save();
+ GxICR_u8(irq) = GxICR_DETECT;
+ tmp = GxICR(irq);
+ arch_local_irq_restore(flags);
+}
+
+static void __mask_and_set_icr(unsigned int irq,
+ unsigned int mask, unsigned int set)
+{
+ unsigned long flags;
u16 tmp;
- *(volatile u8 *) &GxICR(irq) = GxICR_DETECT;
+
+ flags = arch_local_cli_save();
tmp = GxICR(irq);
+ GxICR(irq) = (tmp & mask) | set;
+ tmp = GxICR(irq);
+ arch_local_irq_restore(flags);
}
static void mn10300_cpupic_mask(unsigned int irq)
{
- u16 tmp = GxICR(irq);
- GxICR(irq) = (tmp & GxICR_LEVEL);
- tmp = GxICR(irq);
+ __mask_and_set_icr(irq, GxICR_LEVEL, 0);
}
static void mn10300_cpupic_mask_ack(unsigned int irq)
{
- u16 tmp = GxICR(irq);
- GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT;
- tmp = GxICR(irq);
+#ifdef CONFIG_SMP
+ unsigned long flags;
+ u16 tmp;
+
+ flags = arch_local_cli_save();
+
+ if (!test_and_clear_bit(irq, irq_affinity_request)) {
+ tmp = GxICR(irq);
+ GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT;
+ tmp = GxICR(irq);
+ } else {
+ u16 tmp2;
+ tmp = GxICR(irq);
+ GxICR(irq) = (tmp & GxICR_LEVEL);
+ tmp2 = GxICR(irq);
+
+ irq_affinity_online[irq] = any_online_cpu(*irq_desc[irq].affinity);
+ GxICR(irq) = (tmp & (GxICR_LEVEL | GxICR_ENABLE)) | GxICR_DETECT;
+ tmp = GxICR(irq);
+ }
+
+ arch_local_irq_restore(flags);
+#else /* CONFIG_SMP */
+ __mask_and_set_icr(irq, GxICR_LEVEL, GxICR_DETECT);
+#endif /* CONFIG_SMP */
}
static void mn10300_cpupic_unmask(unsigned int irq)
{
- u16 tmp = GxICR(irq);
- GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE;
- tmp = GxICR(irq);
+ __mask_and_set_icr(irq, GxICR_LEVEL, GxICR_ENABLE);
}
static void mn10300_cpupic_unmask_clear(unsigned int irq)
@@ -56,11 +114,89 @@ static void mn10300_cpupic_unmask_clear(unsigned int irq)
* device has ceased to assert its interrupt line and the interrupt
* channel has been disabled in the PIC, so for level-triggered
* interrupts we need to clear the request bit when we re-enable */
- u16 tmp = GxICR(irq);
- GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT;
- tmp = GxICR(irq);
+#ifdef CONFIG_SMP
+ unsigned long flags;
+ u16 tmp;
+
+ flags = arch_local_cli_save();
+
+ if (!test_and_clear_bit(irq, irq_affinity_request)) {
+ tmp = GxICR(irq);
+ GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT;
+ tmp = GxICR(irq);
+ } else {
+ tmp = GxICR(irq);
+
+ irq_affinity_online[irq] = any_online_cpu(*irq_desc[irq].affinity);
+ GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT;
+ tmp = GxICR(irq);
+ }
+
+ arch_local_irq_restore(flags);
+#else /* CONFIG_SMP */
+ __mask_and_set_icr(irq, GxICR_LEVEL, GxICR_ENABLE | GxICR_DETECT);
+#endif /* CONFIG_SMP */
}
+#ifdef CONFIG_SMP
+static int
+mn10300_cpupic_setaffinity(unsigned int irq, const struct cpumask *mask)
+{
+ unsigned long flags;
+ int err;
+
+ flags = arch_local_cli_save();
+
+ /* check irq no */
+ switch (irq) {
+ case TMJCIRQ:
+ case RESCHEDULE_IPI:
+ case CALL_FUNC_SINGLE_IPI:
+ case LOCAL_TIMER_IPI:
+ case FLUSH_CACHE_IPI:
+ case CALL_FUNCTION_NMI_IPI:
+ case GDB_NMI_IPI:
+#ifdef CONFIG_MN10300_TTYSM0
+ case SC0RXIRQ:
+ case SC0TXIRQ:
+#ifdef CONFIG_MN10300_TTYSM0_TIMER8
+ case TM8IRQ:
+#elif CONFIG_MN10300_TTYSM0_TIMER2
+ case TM2IRQ:
+#endif /* CONFIG_MN10300_TTYSM0_TIMER8 */
+#endif /* CONFIG_MN10300_TTYSM0 */
+
+#ifdef CONFIG_MN10300_TTYSM1
+ case SC1RXIRQ:
+ case SC1TXIRQ:
+#ifdef CONFIG_MN10300_TTYSM1_TIMER12
+ case TM12IRQ:
+#elif CONFIG_MN10300_TTYSM1_TIMER9
+ case TM9IRQ:
+#elif CONFIG_MN10300_TTYSM1_TIMER3
+ case TM3IRQ:
+#endif /* CONFIG_MN10300_TTYSM1_TIMER12 */
+#endif /* CONFIG_MN10300_TTYSM1 */
+
+#ifdef CONFIG_MN10300_TTYSM2
+ case SC2RXIRQ:
+ case SC2TXIRQ:
+ case TM10IRQ:
+#endif /* CONFIG_MN10300_TTYSM2 */
+ err = -1;
+ break;
+
+ default:
+ set_bit(irq, irq_affinity_request);
+ err = 0;
+ break;
+ }
+
+ arch_local_irq_restore(flags);
+ return err;
+}
+#endif /* CONFIG_SMP */
+
/*
* MN10300 PIC level-triggered IRQ handling.
*
@@ -79,6 +215,9 @@ static struct irq_chip mn10300_cpu_pic_level = {
.mask = mn10300_cpupic_mask,
.mask_ack = mn10300_cpupic_mask,
.unmask = mn10300_cpupic_unmask_clear,
+#ifdef CONFIG_SMP
+ .set_affinity = mn10300_cpupic_setaffinity,
+#endif /* CONFIG_SMP */
};
/*
@@ -94,6 +233,9 @@ static struct irq_chip mn10300_cpu_pic_edge = {
.mask = mn10300_cpupic_mask,
.mask_ack = mn10300_cpupic_mask_ack,
.unmask = mn10300_cpupic_unmask,
+#ifdef CONFIG_SMP
+ .set_affinity = mn10300_cpupic_setaffinity,
+#endif /* CONFIG_SMP */
};
/*
@@ -111,14 +253,34 @@ void ack_bad_irq(int irq)
*/
void set_intr_level(int irq, u16 level)
{
- u16 tmp;
+ BUG_ON(in_interrupt());
- if (in_interrupt())
- BUG();
+ __mask_and_set_icr(irq, GxICR_ENABLE, level);
+}
- tmp = GxICR(irq);
- GxICR(irq) = (tmp & GxICR_ENABLE) | level;
- tmp = GxICR(irq);
+void mn10300_intc_set_level(unsigned int irq, unsigned int level)
+{
+ set_intr_level(irq, NUM2GxICR_LEVEL(level) & GxICR_LEVEL);
+}
+
+void mn10300_intc_clear(unsigned int irq)
+{
+ __mask_and_set_icr(irq, GxICR_LEVEL | GxICR_ENABLE, GxICR_DETECT);
+}
+
+void mn10300_intc_set(unsigned int irq)
+{
+ __mask_and_set_icr(irq, 0, GxICR_REQUEST | GxICR_DETECT);
+}
+
+void mn10300_intc_enable(unsigned int irq)
+{
+ mn10300_cpupic_unmask(irq);
+}
+
+void mn10300_intc_disable(unsigned int irq)
+{
+ mn10300_cpupic_mask(irq);
}
/*
@@ -126,7 +288,7 @@ void set_intr_level(int irq, u16 level)
* than before
* - see Documentation/mn10300/features.txt
*/
-void set_intr_postackable(int irq)
+void mn10300_set_lateack_irq_type(int irq)
{
set_irq_chip_and_handler(irq, &mn10300_cpu_pic_level,
handle_level_irq);
@@ -147,6 +309,7 @@ void __init init_IRQ(void)
* interrupts */
set_irq_chip_and_handler(irq, &mn10300_cpu_pic_edge,
handle_level_irq);
+
unit_init_IRQ();
}
@@ -156,6 +319,7 @@ void __init init_IRQ(void)
asmlinkage void do_IRQ(void)
{
unsigned long sp, epsw, irq_disabled_epsw, old_irq_enabled_epsw;
+ unsigned int cpu_id = smp_processor_id();
int irq;
sp = current_stack_pointer();
@@ -163,12 +327,14 @@ asmlinkage void do_IRQ(void)
/* make sure local_irq_enable() doesn't muck up the interrupt priority
* setting in EPSW */
- old_irq_enabled_epsw = __mn10300_irq_enabled_epsw;
+ old_irq_enabled_epsw = __mn10300_irq_enabled_epsw[cpu_id];
local_save_flags(epsw);
- __mn10300_irq_enabled_epsw = EPSW_IE | (EPSW_IM & epsw);
+ __mn10300_irq_enabled_epsw[cpu_id] = EPSW_IE | (EPSW_IM & epsw);
irq_disabled_epsw = EPSW_IE | MN10300_CLI_LEVEL;
- __IRQ_STAT(smp_processor_id(), __irq_count)++;
+#ifdef CONFIG_MN10300_WD_TIMER
+ __IRQ_STAT(cpu_id, __irq_count)++;
+#endif
irq_enter();
@@ -188,7 +354,7 @@ asmlinkage void do_IRQ(void)
local_irq_restore(epsw);
}
- __mn10300_irq_enabled_epsw = old_irq_enabled_epsw;
+ __mn10300_irq_enabled_epsw[cpu_id] = old_irq_enabled_epsw;
irq_exit();
}
@@ -239,11 +405,13 @@ int show_interrupts(struct seq_file *p, void *v)
/* polish off with NMI and error counters */
case NR_IRQS:
+#ifdef CONFIG_MN10300_WD_TIMER
seq_printf(p, "NMI: ");
for (j = 0; j < NR_CPUS; j++)
if (cpu_online(j))
seq_printf(p, "%10u ", nmi_count(j));
seq_putc(p, '\n');
+#endif
seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count));
break;
@@ -251,3 +419,51 @@ int show_interrupts(struct seq_file *p, void *v)
return 0;
}
+
+#ifdef CONFIG_HOTPLUG_CPU
+void migrate_irqs(void)
+{
+ irq_desc_t *desc;
+ int irq;
+ unsigned int self, new;
+ unsigned long flags;
+
+ self = smp_processor_id();
+ for (irq = 0; irq < NR_IRQS; irq++) {
+ desc = irq_desc + irq;
+
+ if (desc->status == IRQ_PER_CPU)
+ continue;
+
+ if (cpu_isset(self, irq_desc[irq].affinity) &&
+ !cpus_intersects(irq_affinity[irq], cpu_online_map)) {
+ int cpu_id;
+ cpu_id = first_cpu(cpu_online_map);
+ cpu_set(cpu_id, irq_desc[irq].affinity);
+ }
+ /* We need to operate irq_affinity_online atomically. */
+ arch_local_cli_save(flags);
+ if (irq_affinity_online[irq] == self) {
+ u16 x, tmp;
+
+ x = CROSS_GxICR(irq, self);
+ CROSS_GxICR(irq, self) = x & GxICR_LEVEL;
+ tmp = CROSS_GxICR(irq, self);
+
+ new = any_online_cpu(irq_desc[irq].affinity);
+ irq_affinity_online[irq] = new;
+
+ CROSS_GxICR(irq, new) =
+ (x & GxICR_LEVEL) | GxICR_DETECT;
+ tmp = CROSS_GxICR(irq, new);
+
+ x &= GxICR_LEVEL | GxICR_ENABLE;
+ if (CROSS_GxICR(irq, self) & GxICR_REQUEST)
+ x |= GxICR_REQUEST | GxICR_DETECT;
+ CROSS_GxICR(irq, new) = x;
+ tmp = CROSS_GxICR(irq, new);
+ }
+ arch_local_irq_restore(flags);
+ }
+}
+#endif /* CONFIG_HOTPLUG_CPU */
OpenPOWER on IntegriCloud