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
- Lines: 369
- Date:
2003-08-25 04:44:40.000000000 -0700
- Orig file:
linux-2.4.21/arch/mips/sibyte/sb1250/irq.c
- Orig date:
2002-11-28 15:53:10.000000000 -0800
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(®s->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)