diff options
Diffstat (limited to 'arch/mips/sgi-ip27/ip27-irq.c')
-rw-r--r-- | arch/mips/sgi-ip27/ip27-irq.c | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/arch/mips/sgi-ip27/ip27-irq.c b/arch/mips/sgi-ip27/ip27-irq.c new file mode 100644 index 000000000..0dde6164a --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-irq.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ip27-irq.c: Highlevel interrupt handling for IP27 architecture. + * + * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org) + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 1999 - 2001 Kanoj Sarcar + */ + +#undef DEBUG + +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/timex.h> +#include <linux/smp.h> +#include <linux/random.h> +#include <linux/kernel.h> +#include <linux/kernel_stat.h> +#include <linux/delay.h> +#include <linux/bitops.h> + +#include <asm/bootinfo.h> +#include <asm/io.h> +#include <asm/mipsregs.h> + +#include <asm/processor.h> +#include <asm/sn/addrs.h> +#include <asm/sn/agent.h> +#include <asm/sn/arch.h> +#include <asm/sn/hub.h> +#include <asm/sn/intr.h> + +/* + * Linux has a controller-independent x86 interrupt architecture. + * every controller has a 'controller-template', that is used + * by the main code to do the right thing. Each driver-visible + * interrupt source is transparently wired to the appropriate + * controller. Thus drivers need not be aware of the + * interrupt-controller. + * + * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC, + * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC. + * (IO-APICs assumed to be messaging to Pentium local-APICs) + * + * the code is designed to be easily extended with new/different + * interrupt controllers, without having to do assembly magic. + */ + +extern asmlinkage void ip27_irq(void); + +/* + * Find first bit set + */ +static int ms1bit(unsigned long x) +{ + int b = 0, s; + + s = 16; if (x >> 16 == 0) s = 0; b += s; x >>= s; + s = 8; if (x >> 8 == 0) s = 0; b += s; x >>= s; + s = 4; if (x >> 4 == 0) s = 0; b += s; x >>= s; + s = 2; if (x >> 2 == 0) s = 0; b += s; x >>= s; + s = 1; if (x >> 1 == 0) s = 0; b += s; + + return b; +} + +/* + * This code is unnecessarily complex, because we do + * intr enabling. Basically, once we grab the set of intrs we need + * to service, we must mask _all_ these interrupts; firstly, to make + * sure the same intr does not intr again, causing recursion that + * can lead to stack overflow. Secondly, we can not just mask the + * one intr we are do_IRQing, because the non-masked intrs in the + * first set might intr again, causing multiple servicings of the + * same intr. This effect is mostly seen for intercpu intrs. + * Kanoj 05.13.00 + */ + +static void ip27_do_irq_mask0(void) +{ + int irq, swlevel; + hubreg_t pend0, mask0; + cpuid_t cpu = smp_processor_id(); + int pi_int_mask0 = + (cputoslice(cpu) == 0) ? PI_INT_MASK0_A : PI_INT_MASK0_B; + + /* copied from Irix intpend0() */ + pend0 = LOCAL_HUB_L(PI_INT_PEND0); + mask0 = LOCAL_HUB_L(pi_int_mask0); + + pend0 &= mask0; /* Pick intrs we should look at */ + if (!pend0) + return; + + swlevel = ms1bit(pend0); +#ifdef CONFIG_SMP + if (pend0 & (1UL << CPU_RESCHED_A_IRQ)) { + LOCAL_HUB_CLR_INTR(CPU_RESCHED_A_IRQ); + scheduler_ipi(); + } else if (pend0 & (1UL << CPU_RESCHED_B_IRQ)) { + LOCAL_HUB_CLR_INTR(CPU_RESCHED_B_IRQ); + scheduler_ipi(); + } else if (pend0 & (1UL << CPU_CALL_A_IRQ)) { + LOCAL_HUB_CLR_INTR(CPU_CALL_A_IRQ); + irq_enter(); + generic_smp_call_function_interrupt(); + irq_exit(); + } else if (pend0 & (1UL << CPU_CALL_B_IRQ)) { + LOCAL_HUB_CLR_INTR(CPU_CALL_B_IRQ); + irq_enter(); + generic_smp_call_function_interrupt(); + irq_exit(); + } else +#endif + { + /* "map" swlevel to irq */ + struct slice_data *si = cpu_data[cpu].data; + + irq = si->level_to_irq[swlevel]; + do_IRQ(irq); + } + + LOCAL_HUB_L(PI_INT_PEND0); +} + +static void ip27_do_irq_mask1(void) +{ + int irq, swlevel; + hubreg_t pend1, mask1; + cpuid_t cpu = smp_processor_id(); + int pi_int_mask1 = (cputoslice(cpu) == 0) ? PI_INT_MASK1_A : PI_INT_MASK1_B; + struct slice_data *si = cpu_data[cpu].data; + + /* copied from Irix intpend0() */ + pend1 = LOCAL_HUB_L(PI_INT_PEND1); + mask1 = LOCAL_HUB_L(pi_int_mask1); + + pend1 &= mask1; /* Pick intrs we should look at */ + if (!pend1) + return; + + swlevel = ms1bit(pend1); + /* "map" swlevel to irq */ + irq = si->level_to_irq[swlevel]; + LOCAL_HUB_CLR_INTR(swlevel); + do_IRQ(irq); + + LOCAL_HUB_L(PI_INT_PEND1); +} + +static void ip27_prof_timer(void) +{ + panic("CPU %d got a profiling interrupt", smp_processor_id()); +} + +static void ip27_hub_error(void) +{ + panic("CPU %d got a hub error interrupt", smp_processor_id()); +} + +asmlinkage void plat_irq_dispatch(void) +{ + unsigned long pending = read_c0_cause() & read_c0_status(); + extern unsigned int rt_timer_irq; + + if (pending & CAUSEF_IP4) + do_IRQ(rt_timer_irq); + else if (pending & CAUSEF_IP2) /* PI_INT_PEND_0 or CC_PEND_{A|B} */ + ip27_do_irq_mask0(); + else if (pending & CAUSEF_IP3) /* PI_INT_PEND_1 */ + ip27_do_irq_mask1(); + else if (pending & CAUSEF_IP5) + ip27_prof_timer(); + else if (pending & CAUSEF_IP6) + ip27_hub_error(); +} + +void __init arch_init_irq(void) +{ +} + +void install_ipi(void) +{ + int slice = LOCAL_HUB_L(PI_CPU_NUM); + int cpu = smp_processor_id(); + struct slice_data *si = cpu_data[cpu].data; + struct hub_data *hub = hub_data(cpu_to_node(cpu)); + int resched, call; + + resched = CPU_RESCHED_A_IRQ + slice; + __set_bit(resched, hub->irq_alloc_mask); + __set_bit(resched, si->irq_enable_mask); + LOCAL_HUB_CLR_INTR(resched); + + call = CPU_CALL_A_IRQ + slice; + __set_bit(call, hub->irq_alloc_mask); + __set_bit(call, si->irq_enable_mask); + LOCAL_HUB_CLR_INTR(call); + + if (slice == 0) { + LOCAL_HUB_S(PI_INT_MASK0_A, si->irq_enable_mask[0]); + LOCAL_HUB_S(PI_INT_MASK1_A, si->irq_enable_mask[1]); + } else { + LOCAL_HUB_S(PI_INT_MASK0_B, si->irq_enable_mask[0]); + LOCAL_HUB_S(PI_INT_MASK1_B, si->irq_enable_mask[1]); + } +} |