summaryrefslogtreecommitdiffstats
path: root/arch/sparc64/kernel/irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc64/kernel/irq.c')
-rw-r--r--arch/sparc64/kernel/irq.c652
1 files changed, 455 insertions, 197 deletions
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
index d8707261f..6eccb883a 100644
--- a/arch/sparc64/kernel/irq.c
+++ b/arch/sparc64/kernel/irq.c
@@ -1,4 +1,4 @@
-/* $Id: irq.c,v 1.66 1998/10/21 15:02:25 ecd Exp $
+/* $Id: irq.c,v 1.76 1999/04/02 14:54:30 davem Exp $
* irq.c: UltraSparc IRQ handling/init/registry.
*
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -41,20 +41,30 @@
#define SA_DMA_SYNC 0x200
#ifdef __SMP__
-void distribute_irqs(void);
-static int irqs_have_been_distributed = 0;
+static void distribute_irqs(void);
#endif
-/* UPA nodes send interrupt packet to UltraSparc with first data reg value
- * low 5 bits holding the IRQ identifier being delivered. We must translate
- * this into a non-vector IRQ so we can set the softint on this cpu. To
- * make things even more swift we store the complete mask here.
+/* UPA nodes send interrupt packet to UltraSparc with first data reg
+ * value low 5 (7 on Starfire) bits holding the IRQ identifier being
+ * delivered. We must translate this into a non-vector IRQ so we can
+ * set the softint on this cpu.
+ *
+ * To make processing these packets efficient and race free we use
+ * an array of irq buckets below. The interrupt vector handler in
+ * entry.S feeds incoming packets into per-cpu pil-indexed lists.
+ * The IVEC handler does not need to act atomically, the PIL dispatch
+ * code uses CAS to get an atomic snapshot of the list and clear it
+ * at the same time.
*/
-#define NUM_HARD_IVECS 2048
-#define NUM_IVECS (NUM_HARD_IVECS + 64) /* For SMP IRQ distribution alg. */
+struct ino_bucket ivector_table[NUM_IVECS] __attribute__ ((aligned (64)));
-unsigned long ivector_to_mask[NUM_IVECS];
+#ifndef __SMP__
+unsigned int __up_workvec[16] __attribute__ ((aligned (64)));
+#define irq_work(__cpu, __pil) &(__up_workvec[(__pil)])
+#else
+#define irq_work(__cpu, __pil) &(cpu_data[(__cpu)].irq_worklists[(__pil)])
+#endif
/* This is based upon code in the 32-bit Sparc kernel written mostly by
* David Redman (djhr@tadpole.co.uk).
@@ -63,30 +73,21 @@ unsigned long ivector_to_mask[NUM_IVECS];
static struct irqaction static_irqaction[MAX_STATIC_ALLOC];
static int static_irq_count = 0;
-/* XXX Must be exported so that fast IRQ handlers can get at it... -DaveM */
+/* This is exported so that fast IRQ handlers can get at it... -DaveM */
struct irqaction *irq_action[NR_IRQS+1] = {
NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL
};
-#define IBF_DMA_SYNC 0x01
-#define IBF_PCI 0x02
-#define IBF_ACTIVE 0x04
+/* Only 8-bits are available, be careful. -DaveM */
+#define IBF_DMA_SYNC 0x01 /* DMA synchronization behind PCI bridge needed. */
+#define IBF_PCI 0x02 /* Indicates PSYCHO/SCHIZO PCI interrupt. */
+#define IBF_ACTIVE 0x04 /* This interrupt is active and has a handler. */
+#define IBF_MULTI 0x08 /* On PCI, indicates shared bucket. */
-#define __imap(bucket) ((bucket)->iclr + (bucket)->imap_off)
#define __bucket(irq) ((struct ino_bucket *)(unsigned long)(irq))
#define __irq(bucket) ((unsigned int)(unsigned long)(bucket))
-static struct ino_bucket *bucket_base, *buckets, *endbuckets;
-
-__initfunc(unsigned long irq_init(unsigned long start_mem, unsigned long end_mem))
-{
- start_mem = (start_mem + 15) & ~15;
- bucket_base = buckets = (struct ino_bucket *)start_mem;
- endbuckets = buckets + 2048;
- return (unsigned long)endbuckets;
-}
-
int get_irq_list(char *buf)
{
int i, len = 0;
@@ -104,7 +105,7 @@ int get_irq_list(char *buf)
#else
for (j = 0; j < smp_num_cpus; j++)
len += sprintf(buf + len, "%10u ",
- kstat.irqs[cpu_logical_map(j)][i]);
+ kstat.irqs[cpu_logical_map(j)][i]);
#endif
len += sprintf(buf + len, "%c %s",
(action->flags & SA_INTERRUPT) ? '+' : ' ',
@@ -224,8 +225,7 @@ unsigned char psycho_ino_to_pil[] = {
1, /* Power Management */
};
-/* INO number to IMAP register offset for PSYCHO external IRQ's.
- */
+/* INO number to IMAP register offset for PSYCHO external IRQ's. */
#define psycho_offset(x) ((unsigned long)(&(((struct psycho_regs *)0)->x)))
#define psycho_imap_offset(ino) \
@@ -241,16 +241,27 @@ unsigned char psycho_ino_to_pil[] = {
/* Now these are always passed a true fully specified sun4u INO. */
void enable_irq(unsigned int irq)
{
+ extern int this_is_starfire;
struct ino_bucket *bucket = __bucket(irq);
- unsigned long tid;
unsigned int *imap;
+ unsigned long tid;
- imap = __imap(bucket);
- if (!imap) return;
+ imap = bucket->imap;
+ if (!imap)
+ return;
- /* We send it to our UPA MID, for SMP this will be different. */
- __asm__ __volatile__("ldxa [%%g0] %1, %0" : "=r" (tid) : "i" (ASI_UPA_CONFIG));
- tid = ((tid & UPA_CONFIG_MID) << 9);
+ if(this_is_starfire == 0) {
+ /* We set it to our UPA MID. */
+ __asm__ __volatile__("ldxa [%%g0] %1, %0"
+ : "=r" (tid)
+ : "i" (ASI_UPA_CONFIG));
+ tid = ((tid & UPA_CONFIG_MID) << 9);
+ } else {
+ extern unsigned int starfire_translate(unsigned int *imap,
+ unsigned int upaid);
+
+ tid = (starfire_translate(imap, current->processor) << 26);
+ }
/* NOTE NOTE NOTE, IGN and INO are read-only, IGN is a product
* of this SYSIO's preconfigured IGN in the SYSIO Control
@@ -269,35 +280,83 @@ void disable_irq(unsigned int irq)
struct ino_bucket *bucket = __bucket(irq);
unsigned int *imap;
- imap = __imap(bucket);
- if (!imap) return;
-
- /* NOTE: We do not want to futz with the IRQ clear registers
- * and move the state to IDLE, the SCSI code does call
- * disable_irq() to assure atomicity in the queue cmd
- * SCSI adapter driver code. Thus we'd lose interrupts.
- */
- *imap &= ~(SYSIO_IMAP_VALID);
+ imap = bucket->imap;
+ if (imap != NULL) {
+ /* NOTE: We do not want to futz with the IRQ clear registers
+ * and move the state to IDLE, the SCSI code does call
+ * disable_irq() to assure atomicity in the queue cmd
+ * SCSI adapter driver code. Thus we'd lose interrupts.
+ */
+ *imap &= ~(SYSIO_IMAP_VALID);
+ }
}
+/* The timer is the one "weird" interrupt which is generated by
+ * the CPU %tick register and not by some normal vectored interrupt
+ * source. To handle this special case, we use this dummy INO bucket.
+ */
+static struct ino_bucket pil0_dummy_bucket = {
+ 0, /* irq_chain */
+ 0, /* pil */
+ 0, /* pending */
+ 0, /* flags */
+ 0, /* __unused */
+ NULL, /* irq_info */
+ NULL, /* iclr */
+ NULL, /* imap */
+};
+
unsigned int build_irq(int pil, int inofixup, unsigned int *iclr, unsigned int *imap)
{
- if (buckets == endbuckets)
- panic("Out of IRQ buckets. Should not happen.\n");
- buckets->pil = pil;
- if (pil && (!iclr || !imap)) {
- prom_printf("Invalid build_irq %d %d %016lx %016lx\n", pil, inofixup, iclr, imap);
+ struct ino_bucket *bucket;
+ int ino;
+
+ if(pil == 0) {
+ if(iclr != NULL || imap != NULL) {
+ prom_printf("Invalid dummy bucket for PIL0 (%p:%p)\n",
+ iclr, imap);
+ prom_halt();
+ }
+ return __irq(&pil0_dummy_bucket);
+ }
+
+ /* RULE: Both must be specified in all other cases. */
+ if (iclr == NULL || imap == NULL) {
+ prom_printf("Invalid build_irq %d %d %016lx %016lx\n",
+ pil, inofixup, iclr, imap);
prom_halt();
}
- if (imap)
- buckets->ino = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)) + inofixup;
- else
- buckets->ino = 0;
-
- buckets->iclr = iclr;
- buckets->flags = 0;
- buckets->imap_off = imap - iclr;
- return __irq(buckets++);
+
+ ino = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)) + inofixup;
+ if(ino > NUM_IVECS) {
+ prom_printf("Invalid INO %04x (%d:%d:%016lx:%016lx)\n",
+ ino, pil, inofixup, iclr, imap);
+ prom_halt();
+ }
+
+ /* Ok, looks good, set it up. Don't touch the irq_chain or
+ * the pending flag.
+ */
+ bucket = &ivector_table[ino];
+ if ((bucket->flags & IBF_ACTIVE) ||
+ (bucket->irq_info != NULL)) {
+ /* This is a gross fatal error if it happens here. */
+ prom_printf("IRQ: Trying to reinit INO bucket, fatal error.\n");
+ prom_printf("IRQ: Request INO %04x (%d:%d:%016lx:%016lx)\n",
+ ino, pil, inofixup, iclr, imap);
+ prom_printf("IRQ: Existing (%d:%016lx:%016lx)\n",
+ bucket->pil, bucket->iclr, bucket->imap);
+ prom_printf("IRQ: Cannot continue, halting...\n");
+ prom_halt();
+ }
+ bucket->imap = imap;
+ bucket->iclr = iclr;
+ bucket->pil = pil;
+ bucket->flags = 0;
+
+ bucket->irq_info = NULL;
+
+ return __irq(bucket);
}
unsigned int sbus_build_irq(void *buscookie, unsigned int ino)
@@ -382,8 +441,44 @@ unsigned int psycho_build_irq(void *buscookie, int imap_off, int ino, int need_d
if(!(ino & 0x20))
inofixup = ino & 0x03;
- bucket = __bucket(build_irq(pil, inofixup, iclr, imap));
-
+ /* First check for sharing. */
+ ino = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)) + inofixup;
+ if (ino > NUM_IVECS) {
+ prom_printf("PSYCHO: Invalid INO %04x (%d:%d:%016lx:%016lx)\n",
+ ino, pil, inofixup, iclr, imap);
+ prom_halt();
+ }
+ bucket = &ivector_table[ino];
+ if(bucket->flags & IBF_ACTIVE) {
+ void *old_handler = bucket->irq_info;
+ unsigned long flags;
+
+ if(old_handler == NULL) {
+ prom_printf("PSYCHO: Active bucket, but no handler.\n");
+ prom_halt();
+ }
+ save_and_cli(flags);
+ if((bucket->flags & IBF_MULTI) == 0) {
+ void **vector;
+
+ vector = kmalloc(sizeof(void *) * 4,
+ GFP_KERNEL);
+
+ /* We might have slept. */
+ if((bucket->flags & IBF_MULTI) != 0) {
+ kfree(vector);
+ } else {
+ vector[0] = old_handler;
+ vector[1] = vector[2] = vector[3] = NULL;
+ bucket->irq_info = vector;
+ bucket->flags |= IBF_MULTI;
+ }
+ }
+ restore_flags(flags);
+ } else {
+ /* Just init the bucket */
+ bucket = __bucket(build_irq(pil, inofixup, iclr, imap));
+ }
if (need_dma_sync)
bucket->flags |= IBF_DMA_SYNC;
@@ -392,6 +487,20 @@ unsigned int psycho_build_irq(void *buscookie, int imap_off, int ino, int need_d
}
#endif
+static void atomic_bucket_insert(struct ino_bucket *bucket)
+{
+ unsigned long pstate;
+ unsigned int *ent;
+
+ __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
+ __asm__ __volatile__("wrpr %0, %1, %%pstate"
+ : : "r" (pstate), "i" (PSTATE_IE));
+ ent = irq_work(smp_processor_id(), bucket->pil);
+ bucket->irq_chain = *ent;
+ *ent = __irq(bucket);
+ __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate));
+}
+
int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags, const char *name, void *dev_id)
{
@@ -400,11 +509,16 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *)
unsigned long flags;
int pending = 0;
- if (irq < 0x400000 || (irq & 0x80000000)) {
- prom_printf("request_irq with old style irq %08x %016lx\n", irq, handler);
- prom_halt();
- }
-
+ if ((bucket != &pil0_dummy_bucket) &&
+ (bucket < &ivector_table[0] ||
+ bucket >= &ivector_table[NUM_IVECS])) {
+ unsigned int *caller;
+
+ __asm__ __volatile__("mov %%i7, %0" : "=r" (caller));
+ printk(KERN_CRIT "request_irq: Old style IRQ registry attempt "
+ "from %p, irq %08x.\n", caller, irq);
+ return -EINVAL;
+ }
if(!handler)
return -EINVAL;
@@ -429,24 +543,26 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *)
}
}
+ save_and_cli(flags);
+
action = *(bucket->pil + irq_action);
if(action) {
if((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ))
for (tmp = action; tmp->next; tmp = tmp->next)
;
- else
+ else {
+ restore_flags(flags);
return -EBUSY;
-
+ }
if((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) {
printk("Attempt to mix fast and slow interrupts on IRQ%d "
"denied\n", bucket->pil);
+ restore_flags(flags);
return -EBUSY;
}
action = NULL; /* Or else! */
}
- save_and_cli(flags);
-
/* If this is flagged as statically allocated then we use our
* private struct which is never freed.
*/
@@ -466,12 +582,65 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *)
return -ENOMEM;
}
- if (irqflags & SA_IMAP_MASKED) {
- pending = ((ivector_to_mask[bucket->ino] & 0x80000000) != 0);
- ivector_to_mask[bucket->ino] = (1 << bucket->pil);
- if(pending)
- ivector_to_mask[bucket->ino] |= 0x80000000;
+ if ((irqflags & SA_IMAP_MASKED) == 0) {
+ bucket->irq_info = action;
bucket->flags |= IBF_ACTIVE;
+ } else {
+ if((bucket->flags & IBF_ACTIVE) != 0) {
+ void *orig = bucket->irq_info;
+ void **vector = NULL;
+
+ if((bucket->flags & IBF_PCI) == 0) {
+ printk("IRQ: Trying to share non-PCI bucket.\n");
+ goto free_and_ebusy;
+ }
+ if((bucket->flags & IBF_MULTI) == 0) {
+ vector = kmalloc(sizeof(void *) * 4, GFP_KERNEL);
+ if(vector == NULL)
+ goto free_and_enomem;
+
+ /* We might have slept. */
+ if ((bucket->flags & IBF_MULTI) != 0) {
+ int ent;
+
+ kfree(vector);
+ vector = (void **)bucket->irq_info;
+ for(ent = 0; ent < 4; ent++) {
+ if (vector[ent] == NULL) {
+ vector[ent] = action;
+ break;
+ }
+ }
+ if (ent == 4)
+ goto free_and_ebusy;
+ } else {
+ vector[0] = orig;
+ vector[1] = action;
+ vector[2] = NULL;
+ vector[3] = NULL;
+ bucket->irq_info = vector;
+ bucket->flags |= IBF_MULTI;
+ }
+ } else {
+ int ent;
+
+ vector = (void **)orig;
+ for(ent = 0; ent < 4; ent++) {
+ if(vector[ent] == NULL) {
+ vector[ent] = action;
+ break;
+ }
+ }
+ if (ent == 4)
+ goto free_and_ebusy;
+ }
+ } else {
+ bucket->irq_info = action;
+ bucket->flags |= IBF_ACTIVE;
+ }
+ pending = bucket->pending;
+ if(pending)
+ bucket->pending = 0;
}
action->mask = (unsigned long) bucket;
@@ -489,15 +658,26 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *)
enable_irq(irq);
/* We ate the IVEC already, this makes sure it does not get lost. */
- if(pending)
+ if(pending) {
+ atomic_bucket_insert(bucket);
set_softint(1 << bucket->pil);
-
+ }
restore_flags(flags);
+
#ifdef __SMP__
- if(irqs_have_been_distributed)
- distribute_irqs();
+ distribute_irqs();
#endif
return 0;
+
+free_and_ebusy:
+ kfree(action);
+ restore_flags(flags);
+ return -EBUSY;
+
+free_and_enomem:
+ kfree(action);
+ restore_flags(flags);
+ return -ENOMEM;
}
void free_irq(unsigned int irq, void *dev_id)
@@ -507,9 +687,15 @@ void free_irq(unsigned int irq, void *dev_id)
unsigned long flags;
struct ino_bucket *bucket = __bucket(irq), *bp;
- if (irq < 0x400000 || (irq & 0x80000000)) {
- prom_printf("free_irq with old style irq %08x\n", irq);
- prom_halt();
+ if ((bucket != &pil0_dummy_bucket) &&
+ (bucket < &ivector_table[0] ||
+ bucket >= &ivector_table[NUM_IVECS])) {
+ unsigned int *caller;
+
+ __asm__ __volatile__("mov %%i7, %0" : "=r" (caller));
+ printk(KERN_CRIT "free_irq: Old style IRQ removal attempt "
+ "from %p, irq %08x.\n", caller, irq);
+ return;
}
action = *(bucket->pil + irq_action);
@@ -545,27 +731,59 @@ void free_irq(unsigned int irq, void *dev_id)
*(bucket->pil + irq_action) = action->next;
if(action->flags & SA_IMAP_MASKED) {
- unsigned int *imap = __imap(bucket);
+ unsigned int *imap = bucket->imap;
+ void **vector, *orig;
+ int ent;
+
+ orig = bucket->irq_info;
+ vector = (void **)orig;
+
+ if ((bucket->flags & IBF_MULTI) != 0) {
+ int other = 0;
+ void *orphan = NULL;
+ for(ent = 0; ent < 4; ent++) {
+ if(vector[ent] == action)
+ vector[ent] = NULL;
+ else if(vector[ent] != NULL) {
+ orphan = vector[ent];
+ other++;
+ }
+ }
- /*
- * Only free when no other shared irq uses this bucket.
- */
- tmp = *(bucket->pil + irq_action);
- for( ; tmp; tmp = tmp->next)
- if ((struct ino_bucket *)tmp->mask == bucket)
+ /* Only free when no other shared irq
+ * uses this bucket.
+ */
+ if(other) {
+ if (other == 1) {
+ /* Convert back to non-shared bucket. */
+ bucket->irq_info = orphan;
+ bucket->flags &= ~(IBF_MULTI);
+ kfree(vector);
+ }
goto out;
+ }
+ } else {
+ bucket->irq_info = NULL;
+ }
- ivector_to_mask[bucket->ino] = 0;
-
+ /* This unique interrupt source is now inactive. */
bucket->flags &= ~IBF_ACTIVE;
- for (bp = bucket_base; bp < endbuckets; bp++)
- if (__imap(bp) == imap && (bp->flags & IBF_ACTIVE))
+
+ /* See if any other buckets share this bucket's IMAP
+ * and are still active.
+ */
+ for(ent = 0; ent < NUM_IVECS; ent++) {
+ bp = &ivector_table[ent];
+ if(bp != bucket &&
+ bp->imap == imap &&
+ (bp->flags & IBF_ACTIVE) != 0)
break;
- /*
- * Only disable when no other sub-irq levels of
- * the same imap are active.
+ }
+
+ /* Only disable when no other sub-irq levels of
+ * the same IMAP are active.
*/
- if (bp == endbuckets)
+ if (ent == NUM_IVECS)
disable_irq(irq);
}
@@ -607,10 +825,10 @@ static void show(char * str)
int cpu = smp_processor_id();
printk("\n%s, CPU %d:\n", str, cpu);
- printk("irq: %d [%d %d]\n",
+ printk("irq: %d [%ld %ld]\n",
atomic_read(&global_irq_count),
cpu_data[0].irq_count, cpu_data[1].irq_count);
- printk("bh: %d [%d %d]\n",
+ printk("bh: %d [%ld %ld]\n",
(spin_is_locked(&global_bh_count) ? 1 : 0),
cpu_data[0].bh_count, cpu_data[1].bh_count);
}
@@ -755,57 +973,56 @@ void __global_restore_flags(unsigned long flags)
#endif /* __SMP__ */
-void report_spurious_ivec(struct pt_regs *regs)
+void catch_disabled_ivec(struct pt_regs *regs)
{
- extern unsigned long ivec_spurious_cookie;
-
-#if 0
- printk("IVEC: Spurious interrupt vector (%016lx) received at (%016lx)\n",
- ivec_spurious_cookie, regs->tpc);
-#endif
+ int cpu = smp_processor_id();
+ struct ino_bucket *bucket = __bucket(*irq_work(cpu, 0));
/* We can actually see this on Ultra/PCI PCI cards, which are bridges
* to other devices. Here a single IMAP enabled potentially multiple
* unique interrupt sources (which each do have a unique ICLR register.
*
* So what we do is just register that the IVEC arrived, when registered
- * for real the request_irq() code will check the high bit and signal
+ * for real the request_irq() code will check the bit and signal
* a local CPU interrupt for it.
*/
- ivector_to_mask[ivec_spurious_cookie] |= (0x80000000);
+#if 0
+ printk("IVEC: Spurious interrupt vector (%x) received at (%016lx)\n",
+ bucket - &ivector_table[0], regs->tpc);
+#endif
+ *irq_work(cpu, 0) = 0;
+ bucket->pending = 1;
}
-void unexpected_irq(int irq, void *dev_cookie, struct pt_regs *regs)
-{
- int i;
- struct irqaction *action;
- unsigned int cpu_irq;
-
- cpu_irq = irq & NR_IRQS;
- action = *(cpu_irq + irq_action);
-
- prom_printf("Unexpected IRQ[%d]: ", irq);
- prom_printf("PC[%016lx] NPC[%016lx] FP[%016lx]\n",
- regs->tpc, regs->tnpc, regs->u_regs[14]);
-
- if(action) {
- prom_printf("Expecting: ");
- for(i = 0; i < 16; i++) {
- if(action->handler)
- prom_printf("[%s:%d:0x%016lx] ", action->name,
- i, (unsigned long) action->handler);
- }
- }
- prom_printf("AIEEE\n");
- prom_printf("bogus interrupt received\n");
- prom_cmdline ();
-}
+/* Tune this... */
+#define FORWARD_VOLUME 12
void handler_irq(int irq, struct pt_regs *regs)
{
- struct ino_bucket *bucket = NULL;
- struct irqaction *action, *act;
+ struct ino_bucket *bp, *nbp;
int cpu = smp_processor_id();
+#ifdef __SMP__
+ extern int this_is_starfire;
+ int should_forward = (this_is_starfire == 0 &&
+ irq < 10 &&
+ current->pid != 0);
+ unsigned int buddy = 0;
+
+ /* 'cpu' is the MID (ie. UPAID), calculate the MID
+ * of our buddy.
+ */
+ if(should_forward != 0) {
+ buddy = cpu_number_map[cpu] + 1;
+ if (buddy >= NR_CPUS ||
+ (buddy = cpu_logical_map(buddy)) == -1)
+ buddy = cpu_logical_map(0);
+
+ /* Voo-doo programming. */
+ if(cpu_data[buddy].idle_volume < FORWARD_VOLUME)
+ should_forward = 0;
+ buddy <<= 26;
+ }
+#endif
#ifndef __SMP__
/*
@@ -817,30 +1034,55 @@ void handler_irq(int irq, struct pt_regs *regs)
clear_softint(1 << irq);
irq_enter(cpu, irq);
- action = *(irq + irq_action);
kstat.irqs[cpu][irq]++;
- if(!action) {
- unexpected_irq(irq, 0, regs);
- } else {
- act = action;
- do {
- if(act->flags & SA_IMAP_MASKED) {
- bucket = (struct ino_bucket *)act->mask;
- if(!(ivector_to_mask[bucket->ino] & 0x80000000))
- continue;
+
+ /* Sliiiick... */
+#ifndef __SMP__
+ bp = ((irq != 0) ?
+ __bucket(xchg32(irq_work(cpu, irq), 0)) :
+ &pil0_dummy_bucket);
+#else
+ bp = __bucket(xchg32(irq_work(cpu, irq), 0));
+#endif
+ for( ; bp != NULL; bp = nbp) {
+ unsigned char flags = bp->flags;
+
+ nbp = __bucket(bp->irq_chain);
+ if((flags & IBF_ACTIVE) != 0) {
+ if((flags & IBF_MULTI) == 0) {
+ struct irqaction *ap = bp->irq_info;
+ ap->handler(__irq(bp), ap->dev_id, regs);
+ } else {
+ void **vector = (void **)bp->irq_info;
+ int ent;
+ for(ent = 0; ent < 4; ent++) {
+ struct irqaction *ap = vector[ent];
+ if(ap != NULL)
+ ap->handler(__irq(bp), ap->dev_id, regs);
+ }
}
- act->handler(__irq(bucket), act->dev_id, regs);
- } while((act = act->next) != NULL);
- act = action;
- do {
- if(act->flags & SA_IMAP_MASKED) {
- bucket = (struct ino_bucket *)act->mask;
- if(!(ivector_to_mask[bucket->ino] & 0x80000000))
- continue;
- ivector_to_mask[bucket->ino] &= ~(0x80000000);
- *(bucket->iclr) = SYSIO_ICLR_IDLE;
+ /* Only the dummy bucket lacks IMAP/ICLR. */
+ if(bp->pil != 0) {
+#ifdef __SMP__
+ /* Ok, here is what is going on:
+ * 1) Retargeting IRQs on Starfire is very
+ * expensive so just forget about it on them.
+ * 2) Moving around very high priority interrupts
+ * is a losing game.
+ * 3) If the current cpu is idle, interrupts are
+ * useful work, so keep them here. But do not
+ * pass to our neighbour if he is not very idle.
+ */
+ if (should_forward != 0) {
+ /* Push it to our buddy. */
+ should_forward = 0;
+ *(bp->imap) = (buddy | SYSIO_IMAP_VALID);
+ }
+#endif
+ *(bp->iclr) = SYSIO_ICLR_IDLE;
}
- } while((act = act->next) != NULL);
+ } else
+ bp->pending = 1;
}
irq_exit(cpu, irq);
}
@@ -856,10 +1098,13 @@ void sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs)
irq_enter(cpu, irq);
kstat.irqs[cpu][irq]++;
+
+ *(irq_work(cpu, irq)) = 0;
bucket = (struct ino_bucket *)action->mask;
+
floppy_interrupt(irq, dev_cookie, regs);
- ivector_to_mask[bucket->ino] &= ~(0x80000000);
*(bucket->iclr) = SYSIO_ICLR_IDLE;
+
irq_exit(cpu, irq);
}
#endif
@@ -897,11 +1142,21 @@ int request_fast_irq(unsigned int irq,
struct ino_bucket *bucket = __bucket(irq);
unsigned long flags;
- if (irq < 0x400000 || (irq & 0x80000000)) {
- prom_printf("request_irq with old style irq %08x %016lx\n", irq, handler);
- prom_halt();
- }
+ /* No pil0 dummy buckets allowed here. */
+ if (bucket < &ivector_table[0] ||
+ bucket >= &ivector_table[NUM_IVECS]) {
+ unsigned int *caller;
+
+ __asm__ __volatile__("mov %%i7, %0" : "=r" (caller));
+ printk(KERN_CRIT "request_fast_irq: Old style IRQ registry attempt "
+ "from %p, irq %08x.\n", caller, irq);
+ return -EINVAL;
+ }
+ /* Only IMAP style interrupts can be registered as fast. */
+ if(bucket->pil == 0)
+ return -EINVAL;
+
if(!handler)
return -EINVAL;
@@ -919,6 +1174,7 @@ int request_fast_irq(unsigned int irq,
printk("request_fast_irq: Trying to register yet already owned.\n");
return -EBUSY;
}
+
save_and_cli(flags);
if(irqflags & SA_STATIC_ALLOC) {
if(static_irq_count < MAX_STATIC_ALLOC)
@@ -936,7 +1192,8 @@ int request_fast_irq(unsigned int irq,
}
install_fast_irq(bucket->pil, handler);
- ivector_to_mask[bucket->ino] = (1 << bucket->pil);
+ bucket->irq_info = action;
+ bucket->flags |= IBF_ACTIVE;
action->mask = (unsigned long) bucket;
action->handler = handler;
@@ -949,9 +1206,9 @@ int request_fast_irq(unsigned int irq,
enable_irq(irq);
restore_flags(flags);
+
#ifdef __SMP__
- if(irqs_have_been_distributed)
- distribute_irqs();
+ distribute_irqs();
#endif
return 0;
}
@@ -1025,50 +1282,51 @@ void init_timers(void (*cfunc)(int, void *, struct pt_regs *),
}
#ifdef __SMP__
-/* Called from smp_commence, when we know how many cpus are in the system
- * and can have device IRQ's directed at them.
- */
-/* #define SMP_IRQ_VERBOSE */
-void distribute_irqs(void)
+static int retarget_one_irq(struct irqaction *p, int goal_cpu)
+{
+ extern int this_is_starfire;
+ struct ino_bucket *bucket = __bucket(p->mask);
+ unsigned int *imap = bucket->imap;
+ unsigned int tid;
+
+ /* Never change this, it causes problems on Ex000 systems. */
+ if (bucket->pil == 12)
+ return goal_cpu;
+
+ if(this_is_starfire == 0) {
+ tid = __cpu_logical_map[goal_cpu] << 26;
+ } else {
+ extern unsigned int starfire_translate(unsigned int *imap,
+ unsigned int upaid);
+
+ tid = (starfire_translate(imap, __cpu_logical_map[goal_cpu]) << 26);
+ }
+ *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID);
+
+ goal_cpu++;
+ if(goal_cpu >= NR_CPUS ||
+ __cpu_logical_map[goal_cpu] == -1)
+ goal_cpu = 0;
+ return goal_cpu;
+}
+
+/* Called from request_irq. */
+static void distribute_irqs(void)
{
unsigned long flags;
int cpu, level;
-#ifdef SMP_IRQ_VERBOSE
- printk("SMP: redistributing interrupts...\n");
-#endif
save_and_cli(flags);
cpu = 0;
for(level = 0; level < NR_IRQS; level++) {
struct irqaction *p = irq_action[level];
-
while(p) {
- if(p->flags & SA_IMAP_MASKED) {
- struct ino_bucket *bucket = (struct ino_bucket *)p->mask;
- unsigned int *imap = __imap(bucket);
- unsigned int val;
- unsigned long tid = __cpu_logical_map[cpu] << 26;
-
- val = *imap;
- *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID);
-
-#ifdef SMP_IRQ_VERBOSE
- printk("SMP: Redirecting IGN[%x] INO[%x] "
- "to cpu %d [%s]\n",
- (val & SYSIO_IMAP_IGN) >> 6,
- (val & SYSIO_IMAP_INO), cpu,
- p->name);
-#endif
-
- cpu++;
- if (cpu >= NR_CPUS || __cpu_logical_map[cpu] == -1)
- cpu = 0;
- }
+ if(p->flags & SA_IMAP_MASKED)
+ cpu = retarget_one_irq(p, cpu);
p = p->next;
}
}
restore_flags(flags);
- irqs_have_been_distributed = 1;
}
#endif
@@ -1146,13 +1404,13 @@ __initfunc(void init_IRQ(void))
static int called = 0;
if (called == 0) {
- int i;
-
called = 1;
map_prom_timers();
kill_prom_timer();
- for(i = 0; i < NUM_IVECS; i++)
- ivector_to_mask[i] = 0;
+ memset(&ivector_table[0], 0, sizeof(ivector_table));
+#ifndef __SMP__
+ memset(&__up_workvec[0], 0, sizeof(__up_workvec));
+#endif
}
/* We need to clear any IRQ's pending in the soft interrupt