summaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel/sun4d_irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc/kernel/sun4d_irq.c')
-rw-r--r--arch/sparc/kernel/sun4d_irq.c252
1 files changed, 156 insertions, 96 deletions
diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c
index f22fe1495..302df86f4 100644
--- a/arch/sparc/kernel/sun4d_irq.c
+++ b/arch/sparc/kernel/sun4d_irq.c
@@ -1,8 +1,8 @@
-/* $Id: sun4d_irq.c,v 1.3 1997/12/22 16:09:15 jj Exp $
- * arch/sparc/kernel/sun4d_irq.c:
+/* $Id: sun4d_irq.c,v 1.12 1998/03/19 15:36:36 jj Exp $
+ * arch/sparc/kernel/sun4d_irq.c:
* SS1000/SC2000 interrupt handling.
*
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
* Heavily based on arch/sparc/kernel/irq.c.
*/
@@ -36,32 +36,47 @@
#include <asm/sbus.h>
#include <asm/sbi.h>
+/* If you trust current SCSI layer to handle different SCSI IRQs, enable this. I don't trust it... -jj */
+/* #define DISTRIBUTE_IRQS */
+
struct sun4d_timer_regs *sun4d_timers;
#define TIMER_IRQ 10
#define MAX_STATIC_ALLOC 4
extern struct irqaction static_irqaction[MAX_STATIC_ALLOC];
extern int static_irq_count;
+unsigned char cpu_leds[32];
+#ifdef __SMP__
+unsigned char sbus_tid[32];
+#endif
extern struct irqaction *irq_action[];
struct sbus_action {
struct irqaction *action;
- unsigned char lock;
- unsigned char active;
- unsigned char disabled;
+ /* For SMP this needs to be extended */
} *sbus_actions;
static int pil_to_sbus[] = {
0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0,
};
+static int sbus_to_pil[] = {
+ 0, 2, 3, 5, 7, 9, 11, 13,
+};
+
static int nsbi;
+#ifdef __SMP__
+spinlock_t sun4d_imsk_lock = SPIN_LOCK_UNLOCKED;
+#endif
int sun4d_get_irq_list(char *buf)
{
int i, j = 0, k = 0, len = 0, sbusl;
struct irqaction * action;
+#ifdef __SMP__
+ int x;
+#endif
for (i = 0 ; i < NR_IRQS ; i++) {
sbusl = pil_to_sbus[i];
@@ -77,8 +92,15 @@ int sun4d_get_irq_list(char *buf)
}
continue;
}
-found_it: len += sprintf(buf+len, "%2d: %8d %c %s",
- i, kstat.interrupts[i],
+found_it: len += sprintf(buf+len, "%3d: ", i);
+#ifndef __SMP__
+ len += sprintf(buf+len, "%10u ", kstat_irqs(i));
+#else
+ for (x = 0; x < smp_num_cpus; x++)
+ len += sprintf(buf+len, "%10u ",
+ kstat.irqs[cpu_logical_map(x)][i]);
+#endif
+ len += sprintf(buf+len, "%c %s",
(action->flags & SA_INTERRUPT) ? '+' : ' ',
action->name);
action = action->next;
@@ -172,7 +194,7 @@ void sun4d_handler_irq(int irq, struct pt_regs * regs)
cc_set_iclr(1 << irq);
irq_enter(cpu, irq, regs);
- kstat.interrupts[irq]++;
+ kstat.irqs[cpu][irq]++;
if (!sbusl) {
action = *(irq + irq_action);
if (!action)
@@ -183,7 +205,6 @@ void sun4d_handler_irq(int irq, struct pt_regs * regs)
} while (action);
} else {
int bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff;
- int lock;
int sbino;
struct sbus_action *actionp;
unsigned mask, slot;
@@ -202,19 +223,13 @@ void sun4d_handler_irq(int irq, struct pt_regs * regs)
if (mask & slot) {
mask &= ~slot;
action = actionp->action;
- __asm__ __volatile__ ("ldstub [%1 + 4], %0"
- : "=r" (lock) : "r" (actionp));
- if (!lock) {
- if (!action)
- unexpected_irq(irq, 0, regs);
- do {
- action->handler(irq, action->dev_id, regs);
- action = action->next;
- } while (action);
- actionp->lock = 0;
- } else
- actionp->active = 1;
+ if (!action)
+ unexpected_irq(irq, 0, regs);
+ do {
+ action->handler(irq, action->dev_id, regs);
+ action = action->next;
+ } while (action);
release_sbi(SBI2DEVID(sbino), slot);
}
}
@@ -305,79 +320,49 @@ int sun4d_request_irq(unsigned int irq,
else
*actionp = action;
- if (ret) irq = *ret;
-
- if (irq > NR_IRQS) {
- struct sbus_action *s = sbus_actions + irq - (1 << 5);
-
- if (s->disabled) {
- s->disabled = 0;
- s->active = 0;
- s->lock = 0;
- }
- }
-
+ enable_irq(irq);
restore_flags(flags);
return 0;
}
static void sun4d_disable_irq(unsigned int irq)
{
- struct sbus_action *s;
-
- if (irq < NR_IRQS) {
- /* FIXME */
- printk ("Unable to disable IRQ %d\n", irq);
- return;
- }
- s = sbus_actions + irq - (1 << 5);
+#ifdef __SMP__
+ int tid = sbus_tid[(irq >> 5) - 1];
+ unsigned long flags;
+#endif
- if (s->disabled) return;
- s->disabled = 1;
- __asm__ __volatile__ ("
-1: ldstub [%0 + 4], %%g1
- orcc %%g1, 0, %%g0
- bne 1b"
- : : "r" (s) : "g1", "cc");
+ if (irq < NR_IRQS) return;
+#ifdef __SMP__
+ spin_lock_irqsave(&sun4d_imsk_lock, flags);
+ cc_set_imsk_other(tid, cc_get_imsk_other(tid) | (1 << sbus_to_pil[(irq >> 2) & 7]));
+ spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
+#else
+ cc_set_imsk(cc_get_imsk() | (1 << sbus_to_pil[(irq >> 2) & 7]));
+#endif
}
static void sun4d_enable_irq(unsigned int irq)
{
- struct sbus_action *s;
- struct irqaction *action;
-
- if (irq < NR_IRQS)
- /* FIXME */
- return;
- s = sbus_actions + irq - (1 << 5);
+#ifdef __SMP__
+ int tid = sbus_tid[(irq >> 5) - 1];
+ unsigned long flags;
+#endif
- if (!s->disabled) return;
- action = s->action;
- s->disabled = 0;
- while (s->active) {
- s->active = 0;
- while (action) {
- /* FIXME: Hope no sbus intr handler uses regs */
- action->handler(irq, action->dev_id, NULL);
- action = action->next;
- }
- }
- s->lock = 0;
+ if (irq < NR_IRQS) return;
+#ifdef __SMP__
+ spin_lock_irqsave(&sun4d_imsk_lock, flags);
+ cc_set_imsk_other(tid, cc_get_imsk_other(tid) & ~(1 << sbus_to_pil[(irq >> 2) & 7]));
+ spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
+#else
+ cc_set_imsk(cc_get_imsk() & ~(1 << sbus_to_pil[(irq >> 2) & 7]));
+#endif
}
#ifdef __SMP__
-
-/* +-------+-------------+-----------+------------------------------------+
- * | bcast | devid | sid | levels mask |
- * +-------+-------------+-----------+------------------------------------+
- * 31 30 23 22 15 14 0
- */
-#define IGEN_MESSAGE(bcast, devid, sid, levels) \
- (((bcast) << 31) | ((devid) << 23) | ((sid) << 15) | (levels))
-
-static void sun4d_send_ipi(int cpu, int level)
+static void sun4d_set_cpu_int(int cpu, int level)
{
- cc_set_igen(IGEN_MESSAGE(0, cpu << 3, 6 + ((level >> 1) & 7), 1 << (level - 1)));
+ sun4d_send_ipi(cpu, level);
}
static void sun4d_clear_ipi(int cpu, int level)
@@ -387,6 +372,55 @@ static void sun4d_clear_ipi(int cpu, int level)
static void sun4d_set_udt(int cpu)
{
}
+
+/* Setup IRQ distribution scheme. */
+__initfunc(void sun4d_distribute_irqs(void))
+{
+#ifdef DISTRIBUTE_IRQS
+ struct linux_sbus *sbus;
+ unsigned long sbus_serving_map;
+
+ sbus_serving_map = cpu_present_map;
+ for_each_sbus(sbus) {
+ if ((sbus->board * 2) == boot_cpu_id && (cpu_present_map & (1 << (sbus->board * 2 + 1))))
+ sbus_tid[sbus->board] = (sbus->board * 2 + 1);
+ else if (cpu_present_map & (1 << (sbus->board * 2)))
+ sbus_tid[sbus->board] = (sbus->board * 2);
+ else if (cpu_present_map & (1 << (sbus->board * 2 + 1)))
+ sbus_tid[sbus->board] = (sbus->board * 2 + 1);
+ else
+ sbus_tid[sbus->board] = 0xff;
+ if (sbus_tid[sbus->board] != 0xff)
+ sbus_serving_map &= ~(1 << sbus_tid[sbus->board]);
+ }
+ for_each_sbus(sbus)
+ if (sbus_tid[sbus->board] == 0xff) {
+ int i = 31;
+
+ if (!sbus_serving_map)
+ sbus_serving_map = cpu_present_map;
+ while (!(sbus_serving_map & (1 << i)))
+ i--;
+ sbus_tid[sbus->board] = i;
+ sbus_serving_map &= ~(1 << i);
+ }
+ for_each_sbus(sbus) {
+ printk("sbus%d IRQs directed to CPU%d\n", sbus->board, sbus_tid[sbus->board]);
+ set_sbi_tid(sbus->devid, sbus_tid[sbus->board] << 3);
+ }
+#else
+ struct linux_sbus *sbus;
+ int cpuid = cpu_logical_map(1);
+
+ if (cpuid == -1)
+ cpuid = cpu_logical_map(0);
+ for_each_sbus(sbus) {
+ sbus_tid[sbus->board] = cpuid;
+ set_sbi_tid(sbus->devid, cpuid << 3);
+ }
+ printk("All sbus IRQs directed to CPU%d\n", cpuid);
+#endif
+}
#endif
static void sun4d_clear_clock_irq(void)
@@ -408,7 +442,7 @@ static void sun4d_load_profile_irq(int cpu, unsigned int limit)
__initfunc(static void sun4d_init_timers(void (*counter_fn)(int, void *, struct pt_regs *)))
{
int irq;
- extern struct prom_cpuinfo linux_cpus[NCPUS];
+ extern struct prom_cpuinfo linux_cpus[NR_CPUS];
int cpu;
/* Map the User Timer registers. */
@@ -431,15 +465,39 @@ __initfunc(static void sun4d_init_timers(void (*counter_fn)(int, void *, struct
/* Enable user timer free run for CPU 0 in BW */
/* bw_set_ctrl(0, bw_get_ctrl(0) | BW_CTRL_USER_TIMER); */
- for(cpu = 0; cpu < NCPUS; cpu++)
- sun4d_load_profile_irq(linux_cpus[cpu].mid, 0);
+ for(cpu = 0; cpu < linux_num_cpus; cpu++)
+ sun4d_load_profile_irq((linux_cpus[cpu].mid >> 3), 0);
+
+#ifdef __SMP__
+ {
+ unsigned long flags;
+ extern unsigned long lvl14_save[4];
+ struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)];
+ extern unsigned int real_irq_entry[], smp4d_ticker[];
+ extern unsigned int patchme_maybe_smp_msg[];
+
+ /* Adjust so that we jump directly to smp4d_ticker */
+ lvl14_save[2] += smp4d_ticker - real_irq_entry;
+
+ /* For SMP we use the level 14 ticker, however the bootup code
+ * has copied the firmwares level 14 vector into boot cpu's
+ * trap table, we must fix this now or we get squashed.
+ */
+ __save_and_cli(flags);
+ patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */
+ trap_table->inst_one = lvl14_save[0];
+ trap_table->inst_two = lvl14_save[1];
+ trap_table->inst_three = lvl14_save[2];
+ trap_table->inst_four = lvl14_save[3];
+ local_flush_cache_all();
+ __restore_flags(flags);
+ }
+#endif
}
__initfunc(unsigned long sun4d_init_sbi_irq(unsigned long memory_start))
{
struct linux_sbus *sbus;
- struct sbus_action *s;
- int i;
unsigned mask;
nsbi = 0;
@@ -449,11 +507,13 @@ __initfunc(unsigned long sun4d_init_sbi_irq(unsigned long memory_start))
sbus_actions = (struct sbus_action *)memory_start;
memory_start += (nsbi * 8 * 4 * sizeof(struct sbus_action));
memset (sbus_actions, 0, (nsbi * 8 * 4 * sizeof(struct sbus_action)));
- for (i = 0, s = sbus_actions; i < nsbi * 8 * 4; i++, s++) {
- s->lock = 0xff;
- s->disabled = 1;
- }
for_each_sbus(sbus) {
+#ifdef __SMP__
+ extern unsigned char boot_cpu_id;
+
+ set_sbi_tid(sbus->devid, boot_cpu_id << 3);
+ sbus_tid[sbus->board] = boot_cpu_id;
+#endif
/* Get rid of pending irqs from PROM */
mask = acquire_sbi(sbus->devid, 0xffffffff);
if (mask) {
@@ -468,16 +528,16 @@ __initfunc(void sun4d_init_IRQ(void))
{
__cli();
- enable_irq = sun4d_enable_irq;
- disable_irq = sun4d_disable_irq;
- clear_clock_irq = sun4d_clear_clock_irq;
- clear_profile_irq = sun4d_clear_profile_irq;
- load_profile_irq = sun4d_load_profile_irq;
+ BTFIXUPSET_CALL(enable_irq, sun4d_enable_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(disable_irq, sun4d_disable_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(clear_clock_irq, sun4d_clear_clock_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(clear_profile_irq, sun4d_clear_profile_irq, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM);
init_timers = sun4d_init_timers;
#ifdef __SMP__
- set_cpu_int = (void (*) (int, int))sun4d_send_ipi;
- clear_cpu_int = (void (*) (int, int))sun4d_clear_ipi;
- set_irq_udt = (void (*) (int))sun4d_set_udt;
+ BTFIXUPSET_CALL(set_cpu_int, sun4d_set_cpu_int, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(clear_cpu_int, sun4d_clear_ipi, BTFIXUPCALL_NOP);
+ BTFIXUPSET_CALL(set_irq_udt, sun4d_set_udt, BTFIXUPCALL_NOP);
#endif
/* Cannot enable interrupts until OBP ticker is disabled. */
}