patch-2.4.22 linux-2.4.22/arch/mips/sibyte/sb1250/irq.c

Next file: linux-2.4.22/arch/mips/sibyte/sb1250/irq_handler.S
Previous file: linux-2.4.22/arch/mips/sibyte/sb1250/ide.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.21/arch/mips/sibyte/sb1250/irq.c linux-2.4.22/arch/mips/sibyte/sb1250/irq.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2000, 2001 Broadcom Corporation
+ * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -23,6 +23,7 @@
 #include <linux/spinlock.h>
 #include <linux/mm.h>
 #include <linux/slab.h>
+#include <linux/kernel_stat.h>
 
 #include <asm/errno.h>
 #include <asm/signal.h>
@@ -50,21 +51,37 @@
 static void disable_sb1250_irq(unsigned int irq);
 static unsigned int startup_sb1250_irq(unsigned int irq);
 static void ack_sb1250_irq(unsigned int irq);
+#ifdef CONFIG_SMP
+static void sb1250_set_affinity(unsigned int irq, unsigned long mask);
+#endif
+
+#ifdef CONFIG_SIBYTE_HAS_LDT
+extern unsigned long ldt_eoi_space;
+#endif
 
-#ifdef CONFIG_REMOTE_DEBUG
+#ifdef CONFIG_KGDB
+#include <asm/gdb-stub.h>
 extern void breakpoint(void);
-extern void set_debug_traps(void);
+static int kgdb_irq;
+#ifdef CONFIG_GDB_CONSOLE
+extern void register_gdb_console(void);
+#endif
 
 /* kgdb is on when configured.  Pass "nokgdb" kernel arg to turn it off */
 static int kgdb_flag = 1;
 static int __init nokgdb(char *str)
 {
 	kgdb_flag = 0;
+	return 1;
 }
 __setup("nokgdb", nokgdb);
-#endif
 
-#define NR_IRQS 64
+/* Default to UART1 */
+int kgdb_port = 1;
+#ifdef CONFIG_SIBYTE_SB1250_DUART
+extern char sb1250_duart_present[];
+#endif
+#endif
 
 static struct hw_interrupt_type sb1250_irq_type = {
 	"SB1250-IMR",
@@ -74,9 +91,16 @@
 	disable_sb1250_irq,
 	ack_sb1250_irq,
 	end_sb1250_irq,
+#ifdef CONFIG_SMP
+	sb1250_set_affinity
+#else
 	NULL
+#endif
 };
 
+/* Store the CPU id (not the logical number) */
+int sb1250_irq_owner[SB1250_NR_IRQS];
+
 spinlock_t sb1250_imr_lock = SPIN_LOCK_UNLOCKED;
 
 void sb1250_mask_irq(int cpu, int irq)
@@ -85,9 +109,9 @@
 	u64 cur_ints;
 
 	spin_lock_irqsave(&sb1250_imr_lock, flags);
-	cur_ints = in64(KSEG1 + A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK);
+	cur_ints = __in64(KSEG1 + A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK);
 	cur_ints |= (((u64) 1) << irq);
-	out64(cur_ints, KSEG1 + A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK);
+	__out64(cur_ints, KSEG1 + A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK);
 	spin_unlock_irqrestore(&sb1250_imr_lock, flags);
 }
 
@@ -97,12 +121,61 @@
 	u64 cur_ints;
 
 	spin_lock_irqsave(&sb1250_imr_lock, flags);
-	cur_ints = in64(KSEG1 + A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK);
+	cur_ints = __in64(KSEG1 + A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK);
 	cur_ints &= ~(((u64) 1) << irq);
-	out64(cur_ints, KSEG1 + A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK);
+	__out64(cur_ints, KSEG1 + A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK);
 	spin_unlock_irqrestore(&sb1250_imr_lock, flags);
 }
 
+#ifdef CONFIG_SMP
+static void sb1250_set_affinity(unsigned int irq, unsigned long mask)
+{
+	int i = 0, old_cpu, cpu, int_on;
+	u64 cur_ints;
+	irq_desc_t *desc = irq_desc + irq;
+	unsigned long flags;
+
+	while (mask) {
+		if (mask & 1) {
+			mask >>= 1;
+			break;
+		}
+		mask >>= 1;
+		i++;
+	}
+
+	if (mask) {
+		printk("attempted to set irq affinity for irq %d to multiple CPUs\n", irq);
+		return;
+	}
+
+	/* Convert logical CPU to physical CPU */
+	cpu = cpu_logical_map(i);
+
+	/* Protect against other affinity changers and IMR manipulation */
+	spin_lock_irqsave(&desc->lock, flags);
+	spin_lock(&sb1250_imr_lock);
+
+	/* Swizzle each CPU's IMR (but leave the IP selection alone) */
+	old_cpu = sb1250_irq_owner[irq];
+	cur_ints = __in64(KSEG1 + A_IMR_MAPPER(old_cpu) + R_IMR_INTERRUPT_MASK);
+	int_on = !(cur_ints & (((u64) 1) << irq));
+	if (int_on) {
+		/* If it was on, mask it */
+		cur_ints |= (((u64) 1) << irq);
+		__out64(cur_ints, KSEG1 + A_IMR_MAPPER(old_cpu) + R_IMR_INTERRUPT_MASK);
+	}
+	sb1250_irq_owner[irq] = cpu;
+	if (int_on) {
+		/* unmask for the new CPU */
+		cur_ints = __in64(KSEG1 + A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK);
+		cur_ints &= ~(((u64) 1) << irq);
+		__out64(cur_ints, KSEG1 + A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK);
+	}
+	spin_unlock(&sb1250_imr_lock);
+	spin_unlock_irqrestore(&desc->lock, flags);
+}
+#endif
 
 
 /* Defined in arch/mips/sibyte/sb1250/irq_handler.S */
@@ -112,7 +185,7 @@
 
 static unsigned int startup_sb1250_irq(unsigned int irq)
 {
-	sb1250_unmask_irq(0, irq);
+	sb1250_unmask_irq(sb1250_irq_owner[irq], irq);
 
 	return 0;		/* never anything pending */
 }
@@ -120,36 +193,58 @@
 
 static void disable_sb1250_irq(unsigned int irq)
 {
-	sb1250_mask_irq(0, irq);
+	sb1250_mask_irq(sb1250_irq_owner[irq], irq);
 }
 
 static void enable_sb1250_irq(unsigned int irq)
 {
-	sb1250_unmask_irq(0, irq);
+	sb1250_unmask_irq(sb1250_irq_owner[irq], irq);
 }
 
 
 static void ack_sb1250_irq(unsigned int irq)
 {
+#ifdef CONFIG_SIBYTE_HAS_LDT
 	u64 pending;
 
 	/*
-	 * If the interrupt was an LDT interrupt, now is the time
-	 * to clear it.
+	 * If the interrupt was an HT interrupt, now is the time to
+	 * clear it.  NOTE: we assume the HT bridge was set up to
+	 * deliver the interrupts to all CPUs (which makes affinity
+	 * changing easier for us)
 	 */
-	pending = in64(KSEG1 + A_IMR_REGISTER(0,R_IMR_LDT_INTERRUPT));
+	pending = in64(KSEG1 + A_IMR_REGISTER(sb1250_irq_owner[irq],
+					      R_IMR_LDT_INTERRUPT));
 	pending &= ((u64)1 << (irq));
-	if (pending)
-		out64(pending, KSEG1+A_IMR_REGISTER(0,R_IMR_LDT_INTERRUPT_CLR));
-
-	sb1250_mask_irq(0, irq);
+	if (pending) {
+		int i;
+		for (i=0; i<smp_num_cpus; i++) {
+			/*
+			 * Clear for all CPUs so an affinity switch
+			 * doesn't find an old status
+			 */
+			out64(pending, 
+			      KSEG1+A_IMR_REGISTER(cpu_logical_map(i),
+						   R_IMR_LDT_INTERRUPT_CLR));
+		}
+
+		/*
+		 * Generate EOI.  For Pass 1 parts, EOI is a nop.  For
+		 * Pass 2, the LDT world may be edge-triggered, but
+		 * this EOI shouldn't hurt.  If they are
+		 * level-sensitive, the EOI is required.
+		 */
+		*(uint32_t *)(ldt_eoi_space+(irq<<16)+(7<<2)) = 0;
+	}
+#endif
+	sb1250_mask_irq(sb1250_irq_owner[irq], irq);
 }
 
 
 static void end_sb1250_irq(unsigned int irq)
 {
 	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
-		sb1250_unmask_irq(0, irq);
+		sb1250_unmask_irq(sb1250_irq_owner[irq], irq);
 	}
 }
 
@@ -162,7 +257,12 @@
 		irq_desc[i].status = IRQ_DISABLED;
 		irq_desc[i].action = 0;
 		irq_desc[i].depth = 1;
-		irq_desc[i].handler = &sb1250_irq_type;
+		if (i < SB1250_NR_IRQS) {
+			irq_desc[i].handler = &sb1250_irq_type;
+			sb1250_irq_owner[i] = 0;
+		} else {
+			irq_desc[i].handler = &no_irq_type;
+		}
 	}
 }
 
@@ -172,12 +272,12 @@
 }
 
 static struct irqaction sb1250_dummy_action = {
-	handler: sb1250_dummy_handler,
-	flags:   0,
-	mask:    0,
-	name:    "sb1250-private",
-	next:    NULL,
-	dev_id:  0
+	.handler = sb1250_dummy_handler,
+	.flags   = 0,
+	.mask    = 0,
+	.name    = "sb1250-private",
+	.next    = NULL,
+	.dev_id  = 0
 };
 
 int sb1250_steal_irq(int irq)
@@ -186,7 +286,7 @@
 	unsigned long flags;
 	int retval = 0;
 
-	if (irq >= NR_IRQS)
+	if (irq >= SB1250_NR_IRQS)
 		return -EINVAL;
 
 	spin_lock_irqsave(&desc->lock,flags);
@@ -198,6 +298,7 @@
 		desc->depth = 0;
 	}
 	spin_unlock_irqrestore(&desc->lock,flags);
+	return 0;
 }
 
 /*
@@ -235,7 +336,7 @@
 		STATUSF_IP1 | STATUSF_IP0;
 
 	/* Default everything to IP2 */
-	for (i = 0; i < NR_IRQS; i++) {	/* was I0 */
+	for (i = 0; i < SB1250_NR_IRQS; i++) {	/* was I0 */
 		out64(IMR_IP2_VAL,
 		      KSEG1 + A_IMR_REGISTER(0,
 					     R_IMR_INTERRUPT_MAP_BASE) +
@@ -273,44 +374,49 @@
 
 	/*
 	 * Note that the timer interrupts are also mapped, but this is
-	 * done in sb1250_time_init()
+	 * done in sb1250_time_init().  Also, the profiling driver 
+	 * does its own management of IP7.
 	 */
 
-#ifdef CONFIG_BCM1250_PROF
-	imask |= STATUSF_IP7;
-#endif
-#ifdef CONFIG_REMOTE_DEBUG
+#ifdef CONFIG_KGDB
 	imask |= STATUSF_IP6;
 #endif
 	/* Enable necessary IPs, disable the rest */
-	change_cp0_status(ST0_IM, imask);
+	change_c0_status(ST0_IM, imask);
 	set_except_vector(0, sb1250_irq_handler);
 
-#ifdef CONFIG_REMOTE_DEBUG
+#ifdef CONFIG_KGDB
 	if (kgdb_flag) {
+		kgdb_irq = K_INT_UART_0 + kgdb_port;
+
+#ifdef CONFIG_SIBYTE_SB1250_DUART	
+		sb1250_duart_present[kgdb_port] = 0;
+#endif
 		/* Setup uart 1 settings, mapper */
-		out64(M_DUART_IMR_BRK, KSEG1 + A_DUART + R_DUART_IMR_B);
+		out64(M_DUART_IMR_BRK, IO_SPACE_BASE + A_DUART_IMRREG(kgdb_port));
 
+		sb1250_steal_irq(kgdb_irq);
 		out64(IMR_IP6_VAL,
-			KSEG1 + A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) + (K_INT_UART_1<<3));
-		tmp = in64(KSEG1 + A_IMR_REGISTER(0, R_IMR_INTERRUPT_MASK));
-		tmp &= ~(1<<K_INT_UART_1);
-		out64(tmp, KSEG1 + A_IMR_REGISTER(0, R_IMR_INTERRUPT_MASK));
+		      IO_SPACE_BASE + A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
+		      (kgdb_irq<<3));
+		sb1250_unmask_irq(0, kgdb_irq);
 
+#ifdef CONFIG_GDB_CONSOLE
+		register_gdb_console();
+#endif
+		prom_printf("Waiting for GDB on UART port %d\n", kgdb_port);
 		set_debug_traps();
 		breakpoint();
 	}
 #endif
 }
 
-#ifdef CONFIG_REMOTE_DEBUG
+#ifdef CONFIG_KGDB
 
 #include <linux/delay.h>
 
-extern void set_async_breakpoint(unsigned int epc);
-
-#define duart_out(reg, val)     out64(val, KSEG1 + A_DUART_CHANREG(1,reg))
-#define duart_in(reg)           in64(KSEG1 + A_DUART_CHANREG(1,reg))
+#define duart_out(reg, val)     csr_out32(val, KSEG1 + A_DUART_CHANREG(kgdb_port,reg))
+#define duart_in(reg)           csr_in32(KSEG1 + A_DUART_CHANREG(kgdb_port,reg))
 
 void sb1250_kgdb_interrupt(struct pt_regs *regs)
 {
@@ -319,10 +425,11 @@
 	 * host to stop the break, since we would see another
 	 * interrupt on the end-of-break too)
 	 */
+	kstat.irqs[smp_processor_id()][kgdb_irq]++;
 	mdelay(500);
 	duart_out(R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT |
 				M_DUART_RX_EN | M_DUART_TX_EN);
-	if (!user_mode(regs))
-		set_async_breakpoint(regs->cp0_epc);
+	set_async_breakpoint(&regs->cp0_epc);
 }
-#endif 	/* CONFIG_REMOTE_DEBUG */
+
+#endif 	/* CONFIG_KGDB */

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