X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fmips%2Fsgi-ip27%2Fip27-irq.c;h=319f8803ef6f13b5dcf8536f469c2fd9784c4cbf;hb=refs%2Fheads%2Fvserver;hp=917c1ee7aef2aa965e38bab7e3ff174d4ef80db3;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/arch/mips/sgi-ip27/ip27-irq.c b/arch/mips/sgi-ip27/ip27-irq.c index 917c1ee7a..319f8803e 100644 --- a/arch/mips/sgi-ip27/ip27-irq.c +++ b/arch/mips/sgi-ip27/ip27-irq.c @@ -5,7 +5,9 @@ * Copyright (C) 1999, 2000 Silicon Graphics, Inc. * Copyright (C) 1999 - 2001 Kanoj Sarcar */ -#include + +#undef DEBUG + #include #include #include @@ -18,16 +20,16 @@ #include #include #include +#include #include #include +#include -#include #include #include #include #include -#include #include #include #include @@ -36,13 +38,6 @@ #include #include -#undef DEBUG_IRQ -#ifdef DEBUG_IRQ -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - /* * Linux has a controller-independent x86 interrupt architecture. * every controller has a 'controller-template', that is used @@ -73,14 +68,15 @@ extern int irq_to_slot[]; static inline int alloc_level(int cpu, int irq) { + struct hub_data *hub = hub_data(cpu_to_node(cpu)); struct slice_data *si = cpu_data[cpu].data; - int level; /* pre-allocated entries */ + int level; - level = find_first_zero_bit(si->irq_alloc_mask, LEVELS_PER_SLICE); + level = find_first_zero_bit(hub->irq_alloc_mask, LEVELS_PER_SLICE); if (level >= LEVELS_PER_SLICE) panic("Cpu %d flooded with devices\n", cpu); - __set_bit(level, si->irq_alloc_mask); + __set_bit(level, hub->irq_alloc_mask); si->level_to_irq[level] = irq; return level; @@ -90,12 +86,9 @@ static inline int find_level(cpuid_t *cpunum, int irq) { int cpu, i; - for (cpu = 0; cpu <= NR_CPUS; cpu++) { + for_each_online_cpu(cpu) { struct slice_data *si = cpu_data[cpu].data; - if (!cpu_online(cpu)) - continue; - for (i = BASE_PCI_IRQ; i < LEVELS_PER_SLICE; i++) if (si->level_to_irq[i] == irq) { *cpunum = cpu; @@ -124,7 +117,7 @@ static int ms1bit(unsigned long x) } /* - * This code is unnecessarily complex, because we do SA_INTERRUPT + * This code is unnecessarily complex, because we do IRQF_DISABLED * 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 @@ -135,7 +128,7 @@ static int ms1bit(unsigned long x) * Kanoj 05.13.00 */ -void ip27_do_irq_mask0(struct pt_regs *regs) +static void ip27_do_irq_mask0(void) { int irq, swlevel; hubreg_t pend0, mask0; @@ -151,9 +144,6 @@ void ip27_do_irq_mask0(struct pt_regs *regs) if (!pend0) return; - /* Prevent any of the picked intrs from recursing */ - LOCAL_HUB_S(pi_int_mask0, mask0 & ~pend0); - swlevel = ms1bit(pend0); #ifdef CONFIG_SMP if (pend0 & (1UL << CPU_RESCHED_A_IRQ)) { @@ -173,18 +163,13 @@ void ip27_do_irq_mask0(struct pt_regs *regs) struct slice_data *si = cpu_data[cpu].data; irq = si->level_to_irq[swlevel]; - do_IRQ(irq, regs); + do_IRQ(irq); } - /* clear bit in pend0 */ - pend0 ^= 1UL << swlevel; - - /* Now allow the set of serviced intrs again */ - LOCAL_HUB_S(pi_int_mask0, mask0); LOCAL_HUB_L(PI_INT_PEND0); } -void ip27_do_irq_mask1(struct pt_regs *regs) +static void ip27_do_irq_mask1(void) { int irq, swlevel; hubreg_t pend1, mask1; @@ -200,28 +185,21 @@ void ip27_do_irq_mask1(struct pt_regs *regs) if (!pend1) return; - /* Prevent any of the picked intrs from recursing */ - LOCAL_HUB_S(pi_int_mask1, mask1 & ~pend1); - swlevel = ms1bit(pend1); /* "map" swlevel to irq */ irq = si->level_to_irq[swlevel]; LOCAL_HUB_CLR_INTR(swlevel); - do_IRQ(irq, regs); - /* clear bit in pend1 */ - pend1 ^= 1UL << swlevel; + do_IRQ(irq); - /* Now allow the set of serviced intrs again */ - LOCAL_HUB_S(pi_int_mask1, mask1); LOCAL_HUB_L(PI_INT_PEND1); } -void ip27_prof_timer(struct pt_regs *regs) +static void ip27_prof_timer(void) { panic("CPU %d got a profiling interrupt", smp_processor_id()); } -void ip27_hub_error(struct pt_regs *regs) +static void ip27_hub_error(void) { panic("CPU %d got a hub error interrupt", smp_processor_id()); } @@ -230,12 +208,11 @@ static int intr_connect_level(int cpu, int bit) { nasid_t nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu)); struct slice_data *si = cpu_data[cpu].data; + unsigned long flags; - __set_bit(bit, si->irq_enable_mask); - - /* Make sure it's not already pending when we connect it. */ - REMOTE_HUB_CLR_INTR(nasid, bit); + set_bit(bit, si->irq_enable_mask); + local_irq_save(flags); if (!cputoslice(cpu)) { REMOTE_HUB_S(nasid, PI_INT_MASK0_A, si->irq_enable_mask[0]); REMOTE_HUB_S(nasid, PI_INT_MASK1_A, si->irq_enable_mask[1]); @@ -243,6 +220,7 @@ static int intr_connect_level(int cpu, int bit) REMOTE_HUB_S(nasid, PI_INT_MASK0_B, si->irq_enable_mask[0]); REMOTE_HUB_S(nasid, PI_INT_MASK1_B, si->irq_enable_mask[1]); } + local_irq_restore(flags); return 0; } @@ -252,7 +230,7 @@ static int intr_disconnect_level(int cpu, int bit) nasid_t nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu)); struct slice_data *si = cpu_data[cpu].data; - __clear_bit(bit, si->irq_enable_mask); + clear_bit(bit, si->irq_enable_mask); if (!cputoslice(cpu)) { REMOTE_HUB_S(nasid, PI_INT_MASK0_A, si->irq_enable_mask[0]); @@ -272,23 +250,21 @@ static unsigned int startup_bridge_irq(unsigned int irq) bridgereg_t device; bridge_t *bridge; int pin, swlevel; + cpuid_t cpu; pin = SLOT_FROM_PCI_IRQ(irq); bc = IRQ_TO_BRIDGE(irq); bridge = bc->base; - DBG("bridge_startup(): irq= 0x%x pin=%d\n", irq, pin); + pr_debug("bridge_startup(): irq= 0x%x pin=%d\n", irq, pin); /* * "map" irq to a swlevel greater than 6 since the first 6 bits * of INT_PEND0 are taken */ - swlevel = alloc_level(bc->irq_cpu, irq); - intr_connect_level(bc->irq_cpu, swlevel); - + swlevel = find_level(&cpu, irq); bridge->b_int_addr[pin].addr = (0x20000 | swlevel | (bc->nasid << 8)); bridge->b_int_enable |= (1 << pin); - /* more stuff in int_enable reg */ - bridge->b_int_enable |= 0x7ffffe00; + bridge->b_int_enable |= 0x7ffffe00; /* more stuff in int_enable */ /* * Enable sending of an interrupt clear packt to the hub on a high to @@ -308,7 +284,7 @@ static unsigned int startup_bridge_irq(unsigned int irq) device |= (pin << (pin*3)); bridge->b_int_device = device; - bridge->b_widget.w_tflush; /* Flush */ + bridge->b_wid_tflush; return 0; /* Never anything pending. */ } @@ -317,12 +293,12 @@ static unsigned int startup_bridge_irq(unsigned int irq) static void shutdown_bridge_irq(unsigned int irq) { struct bridge_controller *bc = IRQ_TO_BRIDGE(irq); + struct hub_data *hub = hub_data(cpu_to_node(bc->irq_cpu)); bridge_t *bridge = bc->base; - struct slice_data *si = cpu_data[bc->irq_cpu].data; int pin, swlevel; cpuid_t cpu; - DBG("bridge_shutdown: irq 0x%x\n", irq); + pr_debug("bridge_shutdown: irq 0x%x\n", irq); pin = SLOT_FROM_PCI_IRQ(irq); /* @@ -332,80 +308,97 @@ static void shutdown_bridge_irq(unsigned int irq) swlevel = find_level(&cpu, irq); intr_disconnect_level(cpu, swlevel); - __clear_bit(swlevel, si->irq_alloc_mask); - si->level_to_irq[swlevel] = -1; + __clear_bit(swlevel, hub->irq_alloc_mask); bridge->b_int_enable &= ~(1 << pin); - bridge->b_widget.w_tflush; /* Flush */ + bridge->b_wid_tflush; } static inline void enable_bridge_irq(unsigned int irq) { - /* All the braindamage happens magically for us in ip27_do_irq */ -} + cpuid_t cpu; + int swlevel; -static void disable_bridge_irq(unsigned int irq) -{ - /* All the braindamage happens magically for us in ip27_do_irq */ + swlevel = find_level(&cpu, irq); /* Criminal offence */ + intr_connect_level(cpu, swlevel); } -static void mask_and_ack_bridge_irq(unsigned int irq) +static inline void disable_bridge_irq(unsigned int irq) { - /* All the braindamage happens magically for us in ip27_do_irq */ -} + cpuid_t cpu; + int swlevel; -static void end_bridge_irq(unsigned int irq) -{ + swlevel = find_level(&cpu, irq); /* Criminal offence */ + intr_disconnect_level(cpu, swlevel); } -static struct hw_interrupt_type bridge_irq_type = { +static struct irq_chip bridge_irq_type = { .typename = "bridge", .startup = startup_bridge_irq, .shutdown = shutdown_bridge_irq, - .enable = enable_bridge_irq, - .disable = disable_bridge_irq, - .ack = mask_and_ack_bridge_irq, - .end = end_bridge_irq, + .ack = disable_bridge_irq, + .mask = disable_bridge_irq, + .mask_ack = disable_bridge_irq, + .unmask = enable_bridge_irq, }; -static unsigned long irq_map[NR_IRQS / BITS_PER_LONG]; +void __devinit register_bridge_irq(unsigned int irq) +{ + set_irq_chip_and_handler(irq, &bridge_irq_type, handle_level_irq); +} -unsigned int allocate_irqno(void) +int __devinit request_bridge_irq(struct bridge_controller *bc) { - int irq; + int irq = allocate_irqno(); + int swlevel, cpu; + nasid_t nasid; -again: - irq = find_first_zero_bit(irq_map, LEVELS_PER_SLICE); + if (irq < 0) + return irq; - if (irq >= NR_IRQS) - return -1; + /* + * "map" irq to a swlevel greater than 6 since the first 6 bits + * of INT_PEND0 are taken + */ + cpu = bc->irq_cpu; + swlevel = alloc_level(cpu, irq); + if (unlikely(swlevel < 0)) { + free_irqno(irq); - if (test_and_set_bit(irq, irq_map)) - goto again; + return -EAGAIN; + } + + /* Make sure it's not already pending when we connect it. */ + nasid = COMPACT_TO_NASID_NODEID(cpu_to_node(cpu)); + REMOTE_HUB_CLR_INTR(nasid, swlevel); + + intr_connect_level(cpu, swlevel); + + register_bridge_irq(irq); return irq; } -void free_irqno(unsigned int irq) +extern void ip27_rt_timer_interrupt(void); + +asmlinkage void plat_irq_dispatch(void) { - clear_bit(irq, irq_map); + unsigned long pending = read_c0_cause() & read_c0_status(); + + if (pending & CAUSEF_IP4) + ip27_rt_timer_interrupt(); + 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 init_IRQ(void) +void __init arch_init_irq(void) { - int i; - - set_except_vector(0, ip27_irq); - - /* - * Right now the bridge irq is our kitchen sink interrupt type - */ - for (i = 0; i <= NR_IRQS; i++) { - irq_desc[i].status = IRQ_DISABLED; - irq_desc[i].action = 0; - irq_desc[i].depth = 1; - irq_desc[i].handler = &bridge_irq_type; - } } void install_ipi(void) @@ -413,25 +406,24 @@ 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; - hubreg_t mask, set; + 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_CLR_INTR(CPU_RESCHED_A_IRQ); - LOCAL_HUB_CLR_INTR(CPU_CALL_A_IRQ); - mask = LOCAL_HUB_L(PI_INT_MASK0_A); /* Slice A */ - set = (1UL << CPU_RESCHED_A_IRQ) | (1UL << CPU_CALL_A_IRQ); - mask |= set; - si->irq_enable_mask[0] |= set; - si->irq_alloc_mask[0] |= set; - LOCAL_HUB_S(PI_INT_MASK0_A, mask); + 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_CLR_INTR(CPU_RESCHED_B_IRQ); - LOCAL_HUB_CLR_INTR(CPU_CALL_B_IRQ); - mask = LOCAL_HUB_L(PI_INT_MASK0_B); /* Slice B */ - set = (1UL << CPU_RESCHED_B_IRQ) | (1UL << CPU_CALL_B_IRQ); - mask |= set; - si->irq_enable_mask[1] |= set; - si->irq_alloc_mask[1] |= set; - LOCAL_HUB_S(PI_INT_MASK0_B, mask); + LOCAL_HUB_S(PI_INT_MASK0_B, si->irq_enable_mask[0]); + LOCAL_HUB_S(PI_INT_MASK1_B, si->irq_enable_mask[1]); } }