diff options
Diffstat (limited to 'arch/mn10300/kernel/irq.c')
| -rw-r--r-- | arch/mn10300/kernel/irq.c | 297 |
1 files changed, 199 insertions, 98 deletions
diff --git a/arch/mn10300/kernel/irq.c b/arch/mn10300/kernel/irq.c index e2d5ed891f3..6ab3b73efcf 100644 --- a/arch/mn10300/kernel/irq.c +++ b/arch/mn10300/kernel/irq.c @@ -12,55 +12,144 @@ #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; +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; /* * MN10300 interrupt controller operations */ -static void mn10300_cpupic_ack(unsigned int irq) +static void mn10300_cpupic_ack(struct irq_data *d) { + unsigned int irq = d->irq; + unsigned long flags; u16 tmp; - *(volatile u8 *) &GxICR(irq) = GxICR_DETECT; + + flags = arch_local_cli_save(); + GxICR_u8(irq) = GxICR_DETECT; tmp = GxICR(irq); + arch_local_irq_restore(flags); } -static void mn10300_cpupic_mask(unsigned int irq) +static void __mask_and_set_icr(unsigned int irq, + unsigned int mask, unsigned int set) { - u16 tmp = GxICR(irq); - GxICR(irq) = (tmp & GxICR_LEVEL); + unsigned long flags; + u16 tmp; + + 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_ack(unsigned int irq) +static void mn10300_cpupic_mask(struct irq_data *d) { - u16 tmp = GxICR(irq); - GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT; - tmp = GxICR(irq); + __mask_and_set_icr(d->irq, GxICR_LEVEL, 0); } -static void mn10300_cpupic_unmask(unsigned int irq) +static void mn10300_cpupic_mask_ack(struct irq_data *d) { - u16 tmp = GxICR(irq); - GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE; - tmp = GxICR(irq); + unsigned int irq = d->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] = + cpumask_any_and(d->affinity, cpu_online_mask); + CROSS_GxICR(irq, irq_affinity_online[irq]) = + (tmp & (GxICR_LEVEL | GxICR_ENABLE)) | GxICR_DETECT; + tmp = CROSS_GxICR(irq, irq_affinity_online[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_clear(unsigned int irq) +static void mn10300_cpupic_unmask(struct irq_data *d) { + __mask_and_set_icr(d->irq, GxICR_LEVEL, GxICR_ENABLE); +} + +static void mn10300_cpupic_unmask_clear(struct irq_data *d) +{ + unsigned int irq = d->irq; /* the MN10300 PIC latches its interrupt request bit, even after the * 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] = cpumask_any_and(d->affinity, + cpu_online_mask); + CROSS_GxICR(irq, irq_affinity_online[irq]) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT; + tmp = CROSS_GxICR(irq, irq_affinity_online[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(struct irq_data *d, const struct cpumask *mask, + bool force) +{ + unsigned long flags; + + flags = arch_local_cli_save(); + set_bit(d->irq, irq_affinity_request); + arch_local_irq_restore(flags); + return 0; +} +#endif /* CONFIG_SMP */ + /* * MN10300 PIC level-triggered IRQ handling. * @@ -72,13 +161,16 @@ static void mn10300_cpupic_unmask_clear(unsigned int irq) * mask_ack() is provided), and mask_ack() just masks. */ static struct irq_chip mn10300_cpu_pic_level = { - .name = "cpu_l", - .disable = mn10300_cpupic_mask, - .enable = mn10300_cpupic_unmask_clear, - .ack = NULL, - .mask = mn10300_cpupic_mask, - .mask_ack = mn10300_cpupic_mask, - .unmask = mn10300_cpupic_unmask_clear, + .name = "cpu_l", + .irq_disable = mn10300_cpupic_mask, + .irq_enable = mn10300_cpupic_unmask_clear, + .irq_ack = NULL, + .irq_mask = mn10300_cpupic_mask, + .irq_mask_ack = mn10300_cpupic_mask, + .irq_unmask = mn10300_cpupic_unmask_clear, +#ifdef CONFIG_SMP + .irq_set_affinity = mn10300_cpupic_setaffinity, +#endif }; /* @@ -87,13 +179,16 @@ static struct irq_chip mn10300_cpu_pic_level = { * We use the latch clearing function of the PIC as the 'ACK' function. */ static struct irq_chip mn10300_cpu_pic_edge = { - .name = "cpu_e", - .disable = mn10300_cpupic_mask, - .enable = mn10300_cpupic_unmask, - .ack = mn10300_cpupic_ack, - .mask = mn10300_cpupic_mask, - .mask_ack = mn10300_cpupic_mask_ack, - .unmask = mn10300_cpupic_unmask, + .name = "cpu_e", + .irq_disable = mn10300_cpupic_mask, + .irq_enable = mn10300_cpupic_unmask, + .irq_ack = mn10300_cpupic_ack, + .irq_mask = mn10300_cpupic_mask, + .irq_mask_ack = mn10300_cpupic_mask_ack, + .irq_unmask = mn10300_cpupic_unmask, +#ifdef CONFIG_SMP + .irq_set_affinity = mn10300_cpupic_setaffinity, +#endif }; /* @@ -111,24 +206,18 @@ void ack_bad_irq(int irq) */ void set_intr_level(int irq, u16 level) { - u16 tmp; + BUG_ON(in_interrupt()); - if (in_interrupt()) - BUG(); - - tmp = GxICR(irq); - GxICR(irq) = (tmp & GxICR_ENABLE) | level; - tmp = GxICR(irq); + __mask_and_set_icr(irq, GxICR_ENABLE, level); } /* * mark an interrupt to be ACK'd after interrupt handlers have been run rather * 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, + irq_set_chip_and_handler(irq, &mn10300_cpu_pic_level, handle_level_irq); } @@ -140,13 +229,14 @@ void __init init_IRQ(void) int irq; for (irq = 0; irq < NR_IRQS; irq++) - if (irq_desc[irq].chip == &no_irq_chip) + if (irq_get_chip(irq) == &no_irq_chip) /* due to the PIC latching interrupt requests, even * when the IRQ is disabled, IRQ_PENDING is superfluous * and we can use handle_level_irq() for edge-triggered * interrupts */ - set_irq_chip_and_handler(irq, &mn10300_cpu_pic_edge, + irq_set_chip_and_handler(irq, &mn10300_cpu_pic_edge, handle_level_irq); + unit_init_IRQ(); } @@ -156,20 +246,22 @@ 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(); - if (sp - (sp & ~(THREAD_SIZE - 1)) < STACK_WARN) - BUG(); + BUG_ON(sp - (sp & ~(THREAD_SIZE - 1)) < STACK_WARN); /* 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(); @@ -189,7 +281,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(); } @@ -197,58 +289,67 @@ asmlinkage void do_IRQ(void) /* * Display interrupt management information through /proc/interrupts */ -int show_interrupts(struct seq_file *p, void *v) +int arch_show_interrupts(struct seq_file *p, int prec) { - int i = *(loff_t *) v, j, cpu; - struct irqaction *action; - unsigned long flags; +#ifdef CONFIG_MN10300_WD_TIMER + int j; + + seq_printf(p, "%*s: ", prec, "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, "%*s: ", prec, "ERR"); + seq_printf(p, "%10u\n", atomic_read(&irq_err_count)); + return 0; +} - switch (i) { - /* display column title bar naming CPUs */ - case 0: - seq_printf(p, " "); - for (j = 0; j < NR_CPUS; j++) - if (cpu_online(j)) - seq_printf(p, "CPU%d ", j); - seq_putc(p, '\n'); - break; - - /* display information rows, one per active CPU */ - case 1 ... NR_IRQS - 1: - raw_spin_lock_irqsave(&irq_desc[i].lock, flags); - - action = irq_desc[i].action; - if (action) { - seq_printf(p, "%3d: ", i); - for_each_present_cpu(cpu) - seq_printf(p, "%10u ", kstat_irqs_cpu(i, cpu)); - seq_printf(p, " %14s.%u", irq_desc[i].chip->name, - (GxICR(i) & GxICR_LEVEL) >> - GxICR_LEVEL_SHIFT); - seq_printf(p, " %s", action->name); - - for (action = action->next; - action; - action = action->next) - seq_printf(p, ", %s", action->name); - - seq_putc(p, '\n'); - } +#ifdef CONFIG_HOTPLUG_CPU +void migrate_irqs(void) +{ + int irq; + unsigned int self, new; + unsigned long flags; - raw_spin_unlock_irqrestore(&irq_desc[i].lock, flags); - break; + self = smp_processor_id(); + for (irq = 0; irq < NR_IRQS; irq++) { + struct irq_data *data = irq_get_irq_data(irq); - /* polish off with NMI and error counters */ - case NR_IRQS: - 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'); + if (irqd_is_per_cpu(data)) + continue; - seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); - break; + if (cpumask_test_cpu(self, &data->affinity) && + !cpumask_intersects(&irq_affinity[irq], cpu_online_mask)) { + int cpu_id; + cpu_id = cpumask_first(cpu_online_mask); + cpumask_set_cpu(cpu_id, &data->affinity); + } + /* We need to operate irq_affinity_online atomically. */ + arch_local_cli_save(flags); + if (irq_affinity_online[irq] == self) { + u16 x, tmp; + + x = GxICR(irq); + GxICR(irq) = x & GxICR_LEVEL; + tmp = GxICR(irq); + + new = cpumask_any_and(&data->affinity, + cpu_online_mask); + 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 (GxICR(irq) & GxICR_REQUEST) + x |= GxICR_REQUEST | GxICR_DETECT; + CROSS_GxICR(irq, new) = x; + tmp = CROSS_GxICR(irq, new); + } + arch_local_irq_restore(flags); } - - return 0; } +#endif /* CONFIG_HOTPLUG_CPU */ |
