diff options
Diffstat (limited to 'arch/mn10300/kernel/irq.c')
| -rw-r--r-- | arch/mn10300/kernel/irq.c | 328 |
1 files changed, 224 insertions, 104 deletions
diff --git a/arch/mn10300/kernel/irq.c b/arch/mn10300/kernel/irq.c index 761c434a248..6ab3b73efcf 100644 --- a/arch/mn10300/kernel/irq.c +++ b/arch/mn10300/kernel/irq.c @@ -12,74 +12,183 @@ #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 INTC controller operations + * MN10300 interrupt controller operations */ -static void mn10300_cpupic_disable(unsigned int irq) +static void mn10300_cpupic_ack(struct irq_data *d) { - u16 tmp = GxICR(irq); - GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT; + unsigned int irq = d->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 mn10300_cpupic_enable(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) | GxICR_ENABLE; + 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_ack(unsigned int irq) +static void mn10300_cpupic_mask(struct irq_data *d) { - u16 tmp; - *(volatile u8 *) &GxICR(irq) = GxICR_DETECT; - tmp = GxICR(irq); + __mask_and_set_icr(d->irq, GxICR_LEVEL, 0); } -static void mn10300_cpupic_mask(unsigned int irq) +static void mn10300_cpupic_mask_ack(struct irq_data *d) { - u16 tmp = GxICR(irq); - GxICR(irq) = (tmp & GxICR_LEVEL); - 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_mask_ack(unsigned int irq) +static void mn10300_cpupic_unmask(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, GxICR_ENABLE); } -static void mn10300_cpupic_unmask(unsigned int irq) +static void mn10300_cpupic_unmask_clear(struct irq_data *d) { - u16 tmp = GxICR(irq); - GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT; - tmp = GxICR(irq); + 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 */ +#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 */ } -static void mn10300_cpupic_end(unsigned int irq) +#ifdef CONFIG_SMP +static int +mn10300_cpupic_setaffinity(struct irq_data *d, const struct cpumask *mask, + bool force) { - u16 tmp = GxICR(irq); - GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE; - tmp = GxICR(irq); + 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 */ -static struct irq_chip mn10300_cpu_pic = { - .name = "cpu", - .disable = mn10300_cpupic_disable, - .enable = mn10300_cpupic_enable, - .ack = mn10300_cpupic_ack, - .mask = mn10300_cpupic_mask, - .mask_ack = mn10300_cpupic_mask_ack, - .unmask = mn10300_cpupic_unmask, - .end = mn10300_cpupic_end, +/* + * MN10300 PIC level-triggered IRQ handling. + * + * The PIC has no 'ACK' function per se. It is possible to clear individual + * channel latches, but each latch relatches whether or not the channel is + * masked, so we need to clear the latch when we unmask the channel. + * + * Also for this reason, we don't supply an ack() op (it's unused anyway if + * mask_ack() is provided), and mask_ack() just masks. + */ +static struct irq_chip mn10300_cpu_pic_level = { + .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 +}; + +/* + * MN10300 PIC edge-triggered IRQ handling. + * + * We use the latch clearing function of the PIC as the 'ACK' function. + */ +static struct irq_chip mn10300_cpu_pic_edge = { + .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 }; /* @@ -97,24 +206,19 @@ void ack_bad_irq(int irq) */ void set_intr_level(int irq, u16 level) { - u16 tmp; - - if (in_interrupt()) - BUG(); + BUG_ON(in_interrupt()); - 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_handler(irq, handle_level_irq); + irq_set_chip_and_handler(irq, &mn10300_cpu_pic_level, + handle_level_irq); } /* @@ -125,9 +229,14 @@ void __init init_IRQ(void) int irq; for (irq = 0; irq < NR_IRQS; irq++) - if (irq_desc[irq].chip == &no_irq_type) - set_irq_chip_and_handler(irq, &mn10300_cpu_pic, - handle_edge_irq); + 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 */ + irq_set_chip_and_handler(irq, &mn10300_cpu_pic_edge, + handle_level_irq); + unit_init_IRQ(); } @@ -137,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(); @@ -170,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(); } @@ -178,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: - 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_cpu(cpu).irqs[i]); - 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; - 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 */ |
