patch-2.4.0-test3 linux/arch/mips64/sgi-ip27/ip27-irq.c
Next file: linux/arch/mips64/sgi-ip27/ip27-klconfig.c
Previous file: linux/arch/mips64/sgi-ip27/ip27-init.c
Back to the patch index
Back to the overall index
- Lines: 500
- Date:
Sun Jul 9 22:18:16 2000
- Orig file:
v2.4.0-test2/linux/arch/mips64/sgi-ip27/ip27-irq.c
- Orig date:
Tue May 23 15:31:33 2000
diff -u --recursive --new-file v2.4.0-test2/linux/arch/mips64/sgi-ip27/ip27-irq.c linux/arch/mips64/sgi-ip27/ip27-irq.c
@@ -1,11 +1,9 @@
-/* $Id: ip27-irq.c,v 1.9 2000/03/14 01:39:27 ralf Exp $
- *
+/*
* ip27-irq.c: Highlevel interrupt handling for IP27 architecture.
*
- * Copyright (C) 1999 Ralf Baechle (ralf@gnu.org)
- * Copyright (C) 1999 Silicon Graphics, Inc.
+ * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org)
+ * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
*/
-#include <linux/config.h>
#include <linux/init.h>
#include <linux/errno.h>
@@ -20,11 +18,11 @@
#include <linux/smp_lock.h>
#include <linux/kernel_stat.h>
#include <linux/delay.h>
+#include <linux/irq.h>
#include <asm/bitops.h>
#include <asm/bootinfo.h>
#include <asm/io.h>
-#include <asm/irq.h>
#include <asm/mipsregs.h>
#include <asm/system.h>
@@ -64,9 +62,10 @@
irq_cpustat_t irq_stat [NR_CPUS];
extern asmlinkage void ip27_irq(void);
+extern int irq_to_bus[], irq_to_slot[], bus_to_cpu[];
int (*irq_cannonicalize)(int irq);
-int intr_connect_level(cpuid_t cpu, int bit);
-int intr_disconnect_level(cpuid_t cpu, int bit);
+int intr_connect_level(int cpu, int bit);
+int intr_disconnect_level(int cpu, int bit);
unsigned int local_bh_count[NR_CPUS];
unsigned int local_irq_count[NR_CPUS];
@@ -76,15 +75,17 @@
* we need to map irq's up to at least bit 7 of the INT_MASK0_A register
* since bits 0-6 are pre-allocated for other purposes.
*/
-#define IRQ_TO_SWLEVEL(i) i + 7
-#define SWLEVEL_TO_IRQ(s) s - 7
+#define IRQ_TO_SWLEVEL(cpu, i) i + 7
+#define SWLEVEL_TO_IRQ(cpu, s) s - 7
/*
- * use these macros to get the encoded nasid, widget id, and real irq
+ * use these macros to get the encoded nasid and widget id
* from the irq value
*/
-#define NASID_FROM_IRQ(i) ((i >> 16)&(0xff))
-#define WID_FROM_IRQ(i) ((i >> 8)&(0xff))
-#define IRQ_FROM_IRQ(i) ((i)&(0xff))
+#define IRQ_TO_BUS(i) irq_to_bus[(i)]
+#define IRQ_TO_CPU(i) bus_to_cpu[IRQ_TO_BUS(i)]
+#define NASID_FROM_PCI_IRQ(i) bus_to_nid[IRQ_TO_BUS(i)]
+#define WID_FROM_PCI_IRQ(i) bus_to_wid[IRQ_TO_BUS(i)]
+#define SLOT_FROM_PCI_IRQ(i) irq_to_slot[i]
void disable_irq(unsigned int irq_nr)
{
@@ -104,7 +105,7 @@
int i, len = 0;
struct irqaction * action;
- for (i = 0 ; i < 32 ; i++) {
+ for (i = 0 ; i < NR_IRQS ; i++) {
action = irq_action[i];
if (!action)
continue;
@@ -126,20 +127,18 @@
* do_IRQ handles all normal device IRQ's (the special SMP cross-CPU interrupts
* have their own specific handlers).
*/
-asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
+static void do_IRQ(cpuid_t thiscpu, int irq, struct pt_regs * regs)
{
struct irqaction *action;
- int do_random, cpu;
+ int do_random;
- cpu = smp_processor_id();
- irq_enter(cpu, irq);
- kstat.irqs[cpu][irq]++;
+ irq_enter(thiscpu, irq);
+ kstat.irqs[thiscpu][irq]++;
action = *(irq + irq_action);
if (action) {
if (!(action->flags & SA_INTERRUPT))
__sti();
- action = *(irq + irq_action);
do_random = 0;
do {
do_random |= action->flags;
@@ -150,7 +149,7 @@
add_interrupt_randomness(irq);
__cli();
}
- irq_exit(cpu, irq);
+ irq_exit(thiscpu, irq);
/* unmasking and bottom half handling is done magically for us. */
}
@@ -171,31 +170,46 @@
return b + (int) (x >> 1);
}
-
-/* For now ... */
+
+/*
+ * This code is unnecessarily complex, because we do SA_INTERRUPT
+ * 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
+ */
void ip27_do_irq(struct pt_regs *regs)
{
int irq, swlevel;
hubreg_t pend0, mask0;
- int pi_int_mask0 = ((cputoslice(smp_processor_id()) == 0) ?
+ cpuid_t thiscpu = smp_processor_id();
+ int pi_int_mask0 = ((cputoslice(thiscpu) == 0) ?
PI_INT_MASK0_A : PI_INT_MASK0_B);
/* copied from Irix intpend0() */
while (((pend0 = LOCAL_HUB_L(PI_INT_PEND0)) &
(mask0 = LOCAL_HUB_L(pi_int_mask0))) != 0) {
- pend0 &= mask0;
- do {
- swlevel = ms1bit(pend0);
- LOCAL_HUB_S(pi_int_mask0, mask0 & ~(1 << swlevel));
- LOCAL_HUB_CLR_INTR(swlevel);
- /* "map" swlevel to irq */
- irq = SWLEVEL_TO_IRQ(swlevel);
- do_IRQ(irq, regs);
- /* reset INT_MASK0 register */
+ pend0 &= mask0; /* Pick intrs we should look at */
+ if (pend0) {
+ /* Prevent any of the picked intrs from recursing */
+ LOCAL_HUB_S(pi_int_mask0, mask0 & ~(pend0));
+ do {
+ swlevel = ms1bit(pend0);
+ LOCAL_HUB_CLR_INTR(swlevel);
+ /* "map" swlevel to irq */
+ irq = SWLEVEL_TO_IRQ(thiscpu, swlevel);
+ do_IRQ(thiscpu, irq, regs);
+ /* clear bit in pend0 */
+ pend0 ^= 1ULL << swlevel;
+ } while(pend0);
+ /* Now allow the set of serviced intrs again */
LOCAL_HUB_S(pi_int_mask0, mask0);
- /* clear bit in pend0 */
- pend0 ^= 1ULL << swlevel;
- } while (pend0);
+ LOCAL_HUB_L(PI_INT_PEND0);
+ }
}
}
@@ -203,69 +217,38 @@
/* Startup one of the (PCI ...) IRQs routes over a bridge. */
static unsigned int bridge_startup(unsigned int irq)
{
- bridge_t *bridge;
- int pin, swlevel;
- int real_irq = IRQ_FROM_IRQ(irq);
-
- DBG("bridge_startup(): irq= 0x%x real_irq= %d\n", irq, real_irq);
- bridge = (bridge_t *) NODE_SWIN_BASE(NASID_FROM_IRQ(irq), WID_FROM_IRQ(irq));
-
- /* FIIIIIXME ... Temporary kludge. This knows how interrupts are
- setup in _my_ Origin. */
-
- if (irq != real_irq) /* pci device interrupt */
- switch (real_irq) {
- case IRQ_FROM_IRQ(IOC3_ETH_INT): pin = 2; break;
- default: pin = real_irq; break;
- }
- else
- switch (real_irq) {
- case CPU_RESCHED_A_IRQ:
- case CPU_RESCHED_B_IRQ:
- case CPU_CALL_A_IRQ:
- case CPU_CALL_B_IRQ:
- return 0;
- default: panic("bridge_startup: whoops? %d\n", irq);
- }
-
- /*
- * "map" irq to a swlevel greater than 6 since the first 6 bits
- * of INT_PEND0 are taken
- */
- swlevel = IRQ_TO_SWLEVEL(real_irq);
- intr_connect_level(smp_processor_id(), swlevel);
-
- bridge->b_int_addr[pin].addr = 0x20000 | swlevel;
- bridge->b_int_enable |= (1 << pin);
- /* set more stuff in int_enable reg */
+ bridgereg_t device;
+ bridge_t *bridge;
+ int pin, swlevel;
+ cpuid_t cpu;
+ nasid_t master = NASID_FROM_PCI_IRQ(irq);
+
+ bridge = (bridge_t *) NODE_SWIN_BASE(master, WID_FROM_PCI_IRQ(irq));
+ pin = SLOT_FROM_PCI_IRQ(irq);
+ cpu = IRQ_TO_CPU(irq);
+
+ DBG("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 = IRQ_TO_SWLEVEL(cpu, irq);
+ intr_connect_level(cpu, swlevel);
+
+ bridge->b_int_addr[pin].addr = (0x20000 | swlevel | (master << 8));
+ bridge->b_int_enable |= (1 << pin);
+ /* more stuff in int_enable reg */
bridge->b_int_enable |= 0x7ffffe00;
- if (real_irq < 2 || real_irq==4 || real_irq==5) {
- bridgereg_t device;
-#if 0
- /*
- * Allocate enough RRBs on the bridge for the DMAs.
- * Right now allocating 2 RRBs on the normal channel
- * and 2 on the virtual channel for slot 0 on the bus.
- * And same for slot 1, to get ioc3 eth working.
- */
- Not touching b_even_resp /* boot doesn't go far */
- bridge->b_even_resp = 0xdd99cc88; /* boot doesn't go far */
- bridge->b_even_resp = 0xcccc8888; /* breaks eth0 */
- bridge->b_even_resp = 0xcc88; /* breaks eth0 */
-#endif
- /* Turn on bridge swapping */
- device = bridge->b_device[real_irq].reg;
- device |= BRIDGE_DEV_SWAP_DIR;
- bridge->b_device[real_irq].reg = device;
- /*
- * Associate interrupt pin with device
- * XXX This only works if b_int_device is initialized to 0!
- */
- device = bridge->b_int_device;
- device |= (pin << (real_irq*3));
- bridge->b_int_device = device;
- }
+ /*
+ * XXX This only works if b_int_device is initialized to 0!
+ * We program the bridge to have a 1:1 mapping between devices
+ * (slots) and intr pins.
+ */
+ device = bridge->b_int_device;
+ device |= (pin << (pin*3));
+ bridge->b_int_device = device;
+
bridge->b_widget.w_tflush; /* Flush */
return 0; /* Never anything pending. */
@@ -274,70 +257,25 @@
/* Shutdown one of the (PCI ...) IRQs routes over a bridge. */
static unsigned int bridge_shutdown(unsigned int irq)
{
- bridge_t *bridge;
- int pin, swlevel;
- int real_irq = IRQ_FROM_IRQ(irq);
- struct irqaction **p;
+ bridge_t *bridge;
+ int pin, swlevel;
- bridge = (bridge_t *) NODE_SWIN_BASE(NASID_FROM_IRQ(irq), WID_FROM_IRQ(irq));
+ bridge = (bridge_t *) NODE_SWIN_BASE(NASID_FROM_PCI_IRQ(irq),
+ WID_FROM_PCI_IRQ(irq));
DBG("bridge_shutdown: irq 0x%x\n", irq);
- /* FIIIIIXME ... Temporary kludge. This knows how interrupts are
- setup in _my_ Origin. */
+ pin = SLOT_FROM_PCI_IRQ(irq);
- if (irq != real_irq) /* pci device interrupt */
- switch (real_irq) {
- case IRQ_FROM_IRQ(IOC3_ETH_INT): pin = 2; break;
- default: pin = real_irq; break;
- }
- else
- switch (real_irq) {
- case CPU_RESCHED_A_IRQ:
- case CPU_RESCHED_B_IRQ:
- case CPU_CALL_A_IRQ:
- case CPU_CALL_B_IRQ:
- return 0;
- default: panic("bridge_startup: whoops?");
- }
-
- /*
- * map irq to a swlevel greater than 6 since the first 6 bits
- * of INT_PEND0 are taken
- */
- swlevel = IRQ_TO_SWLEVEL(real_irq);
- intr_disconnect_level(smp_processor_id(), swlevel);
+ /*
+ * map irq to a swlevel greater than 6 since the first 6 bits
+ * of INT_PEND0 are taken
+ */
+ swlevel = IRQ_TO_SWLEVEL(cpu, irq);
+ intr_disconnect_level(smp_processor_id(), swlevel);
- bridge->b_int_enable &= ~(1 << pin);
- bridge->b_widget.w_tflush; /* Flush */
+ bridge->b_int_enable &= ~(1 << pin);
+ bridge->b_widget.w_tflush; /* Flush */
- return 0; /* Never anything pending. */
-}
-
-static void bridge_init(void)
-{
- bridge_t *bridge;
- nasid_t nasid;
- char wid;
- int bus;
-
- nasid = get_nasid();
-
- for (bus=0; bus<num_bridges; bus++) {
- bridge = (bridge_t *) NODE_SWIN_BASE(bus_to_nid[bus],bus_to_wid[bus]);
- /* Hmm... IRIX sets additional bits in the address which are
- documented as reserved in the bridge docs ... */
- bridge->b_int_mode = 0x0; /* Don't clear ints */
-#if 0
- bridge->b_wid_int_upper = 0x000a8000; /* Ints to node 0 */
- bridge->b_wid_int_lower = 0x01000090;
- bridge->b_dir_map = 0xa00000; /* DMA */
-#endif /* shouldn't lower= 0x01800090 ??? */
- bridge->b_wid_int_upper = 0x000a8000; /* Ints to widget A */
- bridge->b_wid_int_lower = 0x01800090;
- bridge->b_dir_map = 0xa00000; /* DMA */
-
- bridge->b_int_enable = 0;
- bridge->b_widget.w_tflush; /* Flush */
- }
+ return 0; /* Never anything pending. */
}
void irq_debug(void)
@@ -346,8 +284,8 @@
printk("bridge->b_int_status = 0x%x\n", bridge->b_int_status);
printk("bridge->b_int_enable = 0x%x\n", bridge->b_int_enable);
- printk("PI_INT_PEND0 = 0x%x\n", LOCAL_HUB_L(PI_INT_PEND0));
- printk("PI_INT_MASK0_A = 0x%x\n", LOCAL_HUB_L(PI_INT_MASK0_A));
+ printk("PI_INT_PEND0 = 0x%lx\n", LOCAL_HUB_L(PI_INT_PEND0));
+ printk("PI_INT_MASK0_A = 0x%lx\n", LOCAL_HUB_L(PI_INT_MASK0_A));
}
int setup_irq(unsigned int irq, struct irqaction *new)
@@ -357,11 +295,15 @@
unsigned long flags;
DBG("setup_irq: 0x%x\n", irq);
+ if (irq >= NR_IRQS) {
+ printk("IRQ array overflow %d\n", irq);
+ while(1);
+ }
if (new->flags & SA_SAMPLE_RANDOM)
rand_initialize_irq(irq);
save_and_cli(flags);
- p = irq_action + IRQ_FROM_IRQ(irq);
+ p = irq_action + irq;
if ((old = *p) != NULL) {
/* Can't share interrupts unless both agree to */
if (!(old->flags & new->flags & SA_SHIRQ)) {
@@ -379,7 +321,7 @@
*p = new;
- if (!shared) {
+ if ((!shared) && (irq >= BASE_PCI_IRQ)) {
bridge_startup(irq);
}
restore_flags(flags);
@@ -395,8 +337,6 @@
struct irqaction *action;
DBG("request_irq(): irq= 0x%x\n", irq);
- if (IRQ_FROM_IRQ(irq) > 9)
- return -EINVAL;
if (!handler)
return -EINVAL;
@@ -424,18 +364,18 @@
struct irqaction * action, **p;
unsigned long flags;
- if (IRQ_FROM_IRQ(irq) > 9) {
+ if (irq >= NR_IRQS) {
printk("Trying to free IRQ%d\n", irq);
return;
}
- for (p = IRQ_FROM_IRQ(irq) + irq_action; (action = *p) != NULL; p = &action->next) {
+ for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
if (action->dev_id != dev_id)
continue;
/* Found it - now free it */
save_and_cli(flags);
*p = action->next;
- if (!irq[irq_action])
+ if (irq >= BASE_PCI_IRQ)
bridge_shutdown(irq);
restore_flags(flags);
kfree(action);
@@ -465,7 +405,6 @@
{
irq_cannonicalize = indy_irq_cannonicalize;
- bridge_init();
set_except_vector(0, ip27_irq);
}
@@ -675,7 +614,7 @@
return hub_intmasks;
}
-int intr_connect_level(cpuid_t cpu, int bit)
+int intr_connect_level(int cpu, int bit)
{
int ip;
int slice = cputoslice(cpu);
@@ -701,7 +640,7 @@
return(0);
}
-int intr_disconnect_level(cpuid_t cpu, int bit)
+int intr_disconnect_level(int cpu, int bit)
{
int ip;
int slice = cputoslice(cpu);
@@ -728,11 +667,14 @@
/* Nothing, the return from intr will work for us */
}
-void install_cpuintr(cpuid_t cpu)
+extern void smp_call_function_interrupt(void);
+
+void install_cpuintr(int cpu)
{
- int irq;
- extern void smp_call_function_interrupt(void);
+#ifdef CONFIG_SMP
+#if (CPUS_PER_NODE == 2)
static int done = 0;
+ int irq;
/*
* This is a hack till we have a pernode irqlist. Currently,
@@ -740,15 +682,13 @@
* cpu irqs.
*/
-#ifdef CONFIG_SMP
-#if (CPUS_PER_NODE == 2)
irq = CPU_RESCHED_A_IRQ + cputoslice(cpu);
- intr_connect_level(cpu, IRQ_TO_SWLEVEL(irq));
+ intr_connect_level(cpu, IRQ_TO_SWLEVEL(cpu, irq));
if (done == 0)
if (request_irq(irq, handle_resched_intr, 0, "resched", 0))
panic("intercpu intr unconnectible\n");
irq = CPU_CALL_A_IRQ + cputoslice(cpu);
- intr_connect_level(cpu, IRQ_TO_SWLEVEL(irq));
+ intr_connect_level(cpu, IRQ_TO_SWLEVEL(cpu, irq));
if (done == 0)
if (request_irq(irq, smp_call_function_interrupt, 0,
"callfunc", 0))
@@ -766,12 +706,12 @@
done = 1;
/* HACK ENDS */
#else /* CPUS_PER_NODE */
- << Bomb! Must redefine this for more than 2 CPUS. >>
+#error Must redefine this for more than 2 CPUS.
#endif /* CPUS_PER_NODE */
#endif /* CONFIG_SMP */
}
-void install_tlbintr(cpuid_t cpu)
+void install_tlbintr(int cpu)
{
int intr_bit = N_INTPEND_BITS + TLB_INTR_A + cputoslice(cpu);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)