patch-2.4.19 linux-2.4.19/arch/mips64/sgi-ip27/ip27-irq.c

Next file: linux-2.4.19/arch/mips64/sgi-ip27/ip27-pci-dma.c
Previous file: linux-2.4.19/arch/mips64/sgi-ip27/ip27-init.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/arch/mips64/sgi-ip27/ip27-irq.c linux-2.4.19/arch/mips64/sgi-ip27/ip27-irq.c
@@ -19,7 +19,6 @@
 #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>
@@ -33,10 +32,13 @@
 #include <asm/pci/bridge.h>
 #include <asm/sn/sn0/hub.h>
 #include <asm/sn/sn0/ip27.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/agent.h>
 #include <asm/sn/arch.h>
 #include <asm/sn/intr.h>
 #include <asm/sn/intr_public.h>
 
+
 #undef DEBUG_IRQ
 #ifdef DEBUG_IRQ
 #define DBG(x...) printk(x)
@@ -66,12 +68,12 @@
  */
 
 extern asmlinkage void ip27_irq(void);
+extern void do_IRQ(int irq, struct pt_regs *regs);
+
 extern int irq_to_bus[], irq_to_slot[], bus_to_cpu[];
 int intr_connect_level(int cpu, int bit);
 int intr_disconnect_level(int cpu, int bit);
 
-unsigned long spurious_count = 0;
-
 /*
  * There is a single intpend register per node, and we want to have
  * distinct levels for intercpu intrs for both cpus A and B on a node.
@@ -122,90 +124,20 @@
 	return(-1);
 }
 
-
-void disable_irq(unsigned int irq_nr)
-{
-	panic("disable_irq() called ...");
-}
-
-void enable_irq(unsigned int irq_nr)
-{
-	panic("enable_irq() called ...");
-}
-
-/* This is stupid for an Origin which can have thousands of IRQs ...  */
-static struct irqaction *irq_action[NR_IRQS];
-
-int get_irq_list(char *buf)
-{
-	int i, len = 0;
-	struct irqaction * action;
-
-	for (i = 0 ; i < NR_IRQS ; i++) {
-		action = irq_action[i];
-		if (!action) 
-			continue;
-		len += sprintf(buf+len, "%2d: %8d %c %s", i, kstat.irqs[0][i],
-		               (action->flags & SA_INTERRUPT) ? '+' : ' ',
-		               action->name);
-		for (action=action->next; action; action = action->next) {
-			len += sprintf(buf+len, ",%s %s",
-			               (action->flags & SA_INTERRUPT)
-			                ? " +" : "",
-			                action->name);
-		}
-		len += sprintf(buf+len, "\n");
-	}
-	return len;
-}
-
-/*
- * do_IRQ handles all normal device IRQ's (the special SMP cross-CPU interrupts
- * have their own specific handlers).
- */
-static void do_IRQ(cpuid_t thiscpu, int irq, struct pt_regs * regs)
-{
-	struct irqaction *action;
-	int do_random;
-
-	irq_enter(thiscpu, irq);
-	kstat.irqs[thiscpu][irq]++;
-
-	action = *(irq + irq_action);
-	if (action) {
-		if (!(action->flags & SA_INTERRUPT))
-			__sti();
-		do_random = 0;
-        	do {
-			do_random |= action->flags;
-			action->handler(irq, action->dev_id, regs);
-			action = action->next;
-        	} while (action);
-		if (do_random & SA_SAMPLE_RANDOM)
-			add_interrupt_randomness(irq);
-		__cli();
-	}
-	irq_exit(thiscpu, irq);
-
-	if (softirq_pending(thiscpu))
-		do_softirq();
-}
-
 /*
  * Find first bit set
  */
 static int ms1bit(unsigned long x)
 {
-	int	b;
+	int b = 0, s;
 
-	if (x >> 32) 	b = 32, x >>= 32;
-	else		b  =  0;
-	if (x >> 16)	b += 16, x >>= 16;
-	if (x >>  8)	b +=  8, x >>=  8;
-	if (x >>  4)	b +=  4, x >>=  4;
-	if (x >>  2)	b +=  2, x >>=  2;
+	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 + (int) (x >> 1);
+	return b;
 }
 
 /*
@@ -239,7 +171,7 @@
 				LOCAL_HUB_CLR_INTR(swlevel);
 				/* "map" swlevel to irq */
 				irq = LEVEL_TO_IRQ(thiscpu, swlevel);
-				do_IRQ(thiscpu, irq, regs);
+				do_IRQ(irq, regs);
 				/* clear bit in pend0 */
 				pend0 ^= 1ULL << swlevel;
 			} while(pend0);
@@ -252,7 +184,7 @@
 
 
 /* Startup one of the (PCI ...) IRQs routes over a bridge.  */
-static unsigned int bridge_startup(unsigned int irq)
+static unsigned int startup_bridge_irq(unsigned int irq)
 {
 	bridgereg_t device;
 	bridge_t *bridge;
@@ -260,6 +192,9 @@
 	cpuid_t cpu;
 	nasid_t master = NASID_FROM_PCI_IRQ(irq);
 
+	if (irq < BASE_PCI_IRQ)
+		return 0;
+
         bridge = (bridge_t *) NODE_SWIN_BASE(master, WID_FROM_PCI_IRQ(irq));
 	pin = SLOT_FROM_PCI_IRQ(irq);
 	cpu = IRQ_TO_CPU(irq);
@@ -292,12 +227,15 @@
 }
 
 /* Shutdown one of the (PCI ...) IRQs routes over a bridge.  */
-static unsigned int bridge_shutdown(unsigned int irq)
+static unsigned int shutdown_bridge_irq(unsigned int irq)
 {
 	bridge_t *bridge;
 	int pin, swlevel;
 	cpuid_t cpu;
 
+	if (irq < BASE_PCI_IRQ)
+		return 0;
+
 	bridge = (bridge_t *) NODE_SWIN_BASE(NASID_FROM_PCI_IRQ(irq), 
 	                                     WID_FROM_PCI_IRQ(irq));
 	DBG("bridge_shutdown: irq 0x%x\n", irq);
@@ -317,312 +255,64 @@
 	return 0;       /* Never anything pending.  */
 }
 
-void irq_debug(void)
+static inline void enable_bridge_irq(unsigned int irq)
 {
-	bridge_t *bridge = (bridge_t *) 0x9200000008000000;
-
-	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%lx\n", LOCAL_HUB_L(PI_INT_PEND0));
-	printk("PI_INT_MASK0_A = 0x%lx\n", LOCAL_HUB_L(PI_INT_MASK0_A));
+	/* All the braindamage happens magically for us in ip27_do_irq */
 }
 
-int setup_irq(unsigned int irq, struct irqaction *new)
+static void disable_bridge_irq(unsigned int irq)
 {
-	int shared = 0;
-	struct irqaction *old, **p;
-	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;
-	if ((old = *p) != NULL) {
-		/* Can't share interrupts unless both agree to */
-		if (!(old->flags & new->flags & SA_SHIRQ)) {
-			restore_flags(flags);
-			return -EBUSY;
-		}
-
-		/* Add new interrupt at end of irq queue */
-		do {
-			p = &old->next;
-			old = *p;
-		} while (old);
-		shared = 1;
-	}
-
-	*p = new;
-
-	if ((!shared) && (irq >= BASE_PCI_IRQ)) {
-		bridge_startup(irq);
-	}
-	restore_flags(flags);
-
-	return 0;
+	/* All the braindamage happens magically for us in ip27_do_irq */
 }
 
-int request_irq(unsigned int irq, 
-		void (*handler)(int, void *, struct pt_regs *),
-		unsigned long irqflags, const char * devname, void *dev_id)
+static void mask_and_ack_bridge_irq(unsigned int irq)
 {
-	int retval;
-	struct irqaction *action;
-
-	DBG("request_irq(): irq= 0x%x\n", irq);
-	if (!handler)
-		return -EINVAL;
-
-	action = (struct irqaction *)kmalloc(sizeof(*action), GFP_KERNEL);
-	if (!action)
-		return -ENOMEM;
-
-	action->handler = handler;
-	action->flags = irqflags;
-	action->mask = 0;
-	action->name = devname;
-	action->next = NULL;
-	action->dev_id = dev_id;
-
-	DBG("request_irq(): %s  devid= 0x%x\n", devname, dev_id);
-	retval = setup_irq(irq, action);
-	DBG("request_irq(): retval= %d\n", retval);
-	if (retval)
-		kfree(action);
-	return retval;
+	/* All the braindamage happens magically for us in ip27_do_irq */
 }
 
-void free_irq(unsigned int irq, void *dev_id)
+static void end_bridge_irq (unsigned int irq)
 {
-	struct irqaction * action, **p;
-	unsigned long flags;
-
-	if (irq >= NR_IRQS) {
-		printk("Trying to free IRQ%d\n", irq);
-		return;
-	}
-	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 >= BASE_PCI_IRQ)
-			bridge_shutdown(irq);
-		restore_flags(flags);
-		kfree(action);
-		return;
-	}
-	printk("Trying to free free IRQ%d\n",irq);
+	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+		enable_bridge_irq(irq);
 }
 
-/* Useless ISA nonsense.  */
-unsigned long probe_irq_on (void)
-{
-	panic("probe_irq_on called!\n");
-	return 0;
-}
+static struct hw_interrupt_type bridge_irq_type = {
+	"bridge",
+	startup_bridge_irq,
+	shutdown_bridge_irq,
+	enable_bridge_irq,
+	disable_bridge_irq,
+	mask_and_ack_bridge_irq,
+	end_bridge_irq
+};
 
-int probe_irq_off (unsigned long irqs)
+void irq_debug(void)
 {
-	return 0;
-}
+	bridge_t *bridge = (bridge_t *) 0x9200000008000000;
 
-void __init init_IRQ(void)
-{
-	set_except_vector(0, ip27_irq);
+	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%lx\n", LOCAL_HUB_L(PI_INT_PEND0));
+	printk("PI_INT_MASK0_A = 0x%lx\n", LOCAL_HUB_L(PI_INT_MASK0_A));
 }
 
-#ifdef CONFIG_SMP
-
-/*
- * This following are the global intr on off routines, copied almost
- * entirely from i386 code.
- */
-
-int global_irq_holder = NO_PROC_ID;
-spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED;
-
-extern void show_stack(unsigned long* esp);
-
-static void show(char * str)
+void __init init_IRQ(void)
 {
 	int i;
-	int cpu = smp_processor_id();
-
-	printk("\n%s, CPU %d:\n", str, cpu);
-	printk("irq:  %d [",irqs_running());
-	for(i=0;i < smp_num_cpus;i++)
-		printk(" %d",local_irq_count(i));
-	printk(" ]\nbh:   %d [",spin_is_locked(&global_bh_lock) ? 1 : 0);
-	for(i=0;i < smp_num_cpus;i++)
-		printk(" %d",local_bh_count(i));
-
-	printk(" ]\nStack dumps:");
-	for(i = 0; i < smp_num_cpus; i++) {
-		if (i == cpu)
-			continue;
-		printk("\nCPU %d:",i);
-		printk("Code not developed yet\n");
-		/* show_stack(0); */
-	}
-	printk("\nCPU %d:",cpu);
-	printk("Code not developed yet\n");
-	/* show_stack(NULL); */
-	printk("\n");
-}
-
-#define MAXCOUNT 		100000000
-#define SYNC_OTHER_CORES(x)	udelay(x+1)
-
-static inline void wait_on_irq(int cpu)
-{
-	int count = MAXCOUNT;
-
-	for (;;) {
-
-		/*
-		 * Wait until all interrupts are gone. Wait
-		 * for bottom half handlers unless we're
-		 * already executing in one..
-		 */
-		if (!irqs_running())
-			if (local_bh_count(cpu) || !spin_is_locked(&global_bh_lock))
-				break;
-
-		/* Duh, we have to loop. Release the lock to avoid deadlocks */
-		spin_unlock(&global_irq_lock);
-
-		for (;;) {
-			if (!--count) {
-				show("wait_on_irq");
-				count = ~0;
-			}
-			__sti();
-			SYNC_OTHER_CORES(cpu);
-			__cli();
-			if (irqs_running())
-				continue;
-			if (spin_is_locked(&global_irq_lock))
-				continue;
-			if (!local_bh_count(cpu) && spin_is_locked(&global_bh_lock))
-				continue;
-			if (spin_trylock(&global_irq_lock))
-				break;
-		}
-	}
-}
 
-void synchronize_irq(void)
-{
-	if (irqs_running()) {
-		/* Stupid approach */
-		cli();
-		sti();
-	}
-}
-
-static inline void get_irqlock(int cpu)
-{
-	if (!spin_trylock(&global_irq_lock)) {
-		/* do we already hold the lock? */
-		if ((unsigned char) cpu == global_irq_holder)
-			return;
-		/* Uhhuh.. Somebody else got it. Wait.. */
-		spin_lock(&global_irq_lock);
-	}
-	/*
-	 * We also to make sure that nobody else is running
-	 * in an interrupt context.
-	 */
-	wait_on_irq(cpu);
+	set_except_vector(0, ip27_irq);
 
 	/*
-	 * Ok, finally..
+	 * Right now the bridge irq is our kitchen sink interrupt type
 	 */
-	global_irq_holder = cpu;
-}
-
-void __global_cli(void)
-{
-	unsigned int flags;
-
-	__save_flags(flags);
-	if (flags & ST0_IE) {
-		int cpu = smp_processor_id();
-		__cli();
-		if (!local_irq_count(cpu))
-			get_irqlock(cpu);
-	}
-}
-
-void __global_sti(void)
-{
-	int cpu = smp_processor_id();
-
-	if (!local_irq_count(cpu))
-		release_irqlock(cpu);
-	__sti();
-}
-
-/*
- * SMP flags value to restore to:
- * 0 - global cli
- * 1 - global sti
- * 2 - local cli
- * 3 - local sti
- */
-unsigned long __global_save_flags(void)
-{
-	int retval;
-	int local_enabled;
-	unsigned long flags;
-	int cpu = smp_processor_id();
-
-	__save_flags(flags);
-	local_enabled = (flags & ST0_IE);
-	/* default to local */
-	retval = 2 + local_enabled;
-
-	/* check for global flags if we're not in an interrupt */
-	if (!local_irq_count(cpu)) {
-		if (local_enabled)
-			retval = 1;
-		if (global_irq_holder == cpu)
-			retval = 0;
+	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;
 	}
-	return retval;
 }
 
-void __global_restore_flags(unsigned long flags)
-{
-	switch (flags) {
-		case 0:
-			__global_cli();
-			break;
-		case 1:
-			__global_sti();
-			break;
-		case 2:
-			__cli();
-			break;
-		case 3:
-			__sti();
-			break;
-		default:
-			printk("global_restore_flags: %08lx\n", flags);
-	}
-}
-
-#endif /* CONFIG_SMP */
-
 /*
  * Get values that vary depending on which CPU and bit we're operating on.
  */
@@ -697,6 +387,39 @@
 	/* Nothing, the return from intr will work for us */
 }
 
+#ifdef CONFIG_SMP
+
+void core_send_ipi(int destid, unsigned int action)
+{
+	int irq;
+
+#if (CPUS_PER_NODE == 2)
+	switch (action) {
+		case SMP_RESCHEDULE_YOURSELF:
+			irq = CPU_RESCHED_A_IRQ;
+			break;
+		case SMP_CALL_FUNCTION:
+			irq = CPU_CALL_A_IRQ;
+			break;
+		default:
+			panic("sendintr");
+	}
+	irq += cputoslice(destid);
+
+	/*
+	 * Convert the compact hub number to the NASID to get the correct
+	 * part of the address space.  Then set the interrupt bit associated
+	 * with the CPU we want to send the interrupt to.
+	 */
+	REMOTE_HUB_SEND_INTR(COMPACT_TO_NASID_NODEID(cputocnode(destid)),
+			FAST_IRQ_TO_LEVEL(irq));
+#else
+	<< Bomb!  Must redefine this for more than 2 CPUS. >>
+#endif
+}
+
+#endif
+
 extern void smp_call_function_interrupt(void);
 
 void install_cpuintr(int cpu)
@@ -715,16 +438,16 @@
 
 		if (request_irq(CPU_RESCHED_A_IRQ, handle_resched_intr, 
 							0, "resched", 0))
-			panic("intercpu intr unconnectible\n");
+			panic("intercpu intr unconnectible");
 		if (request_irq(CPU_RESCHED_B_IRQ, handle_resched_intr, 
 							0, "resched", 0))
-			panic("intercpu intr unconnectible\n");
+			panic("intercpu intr unconnectible");
 		if (request_irq(CPU_CALL_A_IRQ, smp_call_function_interrupt,
 							0, "callfunc", 0))
-			panic("intercpu intr unconnectible\n");
+			panic("intercpu intr unconnectible");
 		if (request_irq(CPU_CALL_B_IRQ, smp_call_function_interrupt,
 							0, "callfunc", 0))
-			panic("intercpu intr unconnectible\n");
+			panic("intercpu intr unconnectible");
 
 		for (j = 0; j < PERNODE_LEVELS; j++)
 			LEVEL_TO_IRQ(0, j) = -1;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)