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.c717
1 files changed, 573 insertions, 144 deletions
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
index f76c27c57..6f4c9dfdf 100644
--- a/arch/sparc64/kernel/irq.c
+++ b/arch/sparc64/kernel/irq.c
@@ -1,4 +1,4 @@
-/* $Id: irq.c,v 1.19 1997/07/24 12:15:04 davem Exp $
+/* $Id: irq.c,v 1.39 1997/08/31 03:11:18 davem Exp $
* irq.c: UltraSparc IRQ handling/init/registry.
*
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -28,8 +28,18 @@
#include <asm/hardirq.h>
#include <asm/softirq.h>
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#include <asm/pbm.h>
+#endif
+
/* Internal flag, should not be visible elsewhere at all. */
-#define SA_SYSIO_MASKED 0x100
+#define SA_IMAP_MASKED 0x100
+
+#ifdef __SMP__
+void distribute_irqs(void);
+static int irqs_have_been_distributed = 0;
+#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
@@ -37,10 +47,39 @@
* make things even more swift we store the complete mask here.
*/
-#define NUM_IVECS 2048 /* XXX may need more on sunfire/wildfire */
+#define NUM_HARD_IVECS 2048
+#define NUM_IVECS (NUM_HARD_IVECS + 64) /* For SMP IRQ distribution alg. */
unsigned long ivector_to_mask[NUM_IVECS];
+struct ino_bucket {
+ struct ino_bucket *next;
+ unsigned int ino;
+ unsigned int *imap;
+ unsigned int *iclr;
+};
+
+#define INO_HASHSZ (NUM_HARD_IVECS >> 2)
+#define NUM_INO_STATIC 4
+static struct ino_bucket *ino_hash[INO_HASHSZ] = { NULL, };
+static struct ino_bucket static_ino_buckets[NUM_INO_STATIC];
+static int static_ino_bucket_count = 0;
+
+static inline struct ino_bucket *__ino_lookup(unsigned int hash, unsigned int ino)
+{
+ struct ino_bucket *ret = ino_hash[hash];
+
+ for(ret = ino_hash[hash]; ret && ret->ino != ino; ret = ret->next)
+ ;
+
+ return ret;
+}
+
+static inline struct ino_bucket *ino_lookup(unsigned int ino)
+{
+ return __ino_lookup((ino & (INO_HASHSZ - 1)), ino);
+}
+
/* This is based upon code in the 32-bit Sparc kernel written mostly by
* David Redman (djhr@tadpole.co.uk).
*/
@@ -73,15 +112,22 @@ int get_irq_list(char *buf)
}
len += sprintf(buf + len, "\n");
}
+#if 0
+#ifdef CONFIG_PCI
+ len += sprintf(buf + len, "ISTAT: PCI[%016lx] OBIO[%016lx]\n",
+ psycho_root->psycho_regs->pci_istate,
+ psycho_root->psycho_regs->obio_istate);
+#endif
+#endif
return len;
}
-/* INO number to Sparc PIL level. */
-unsigned char ino_to_pil[] = {
- 0, 1, 2, 3, 5, 7, 8, 9, /* SBUS slot 0 */
- 0, 1, 2, 3, 5, 7, 8, 9, /* SBUS slot 1 */
- 0, 1, 2, 3, 5, 7, 8, 9, /* SBUS slot 2 */
- 0, 1, 2, 3, 5, 7, 8, 9, /* SBUS slot 3 */
+/* SBUS SYSIO INO number to Sparc PIL level. */
+unsigned char sysio_ino_to_pil[] = {
+ 0, 1, 2, 7, 5, 7, 8, 9, /* SBUS slot 0 */
+ 0, 1, 2, 7, 5, 7, 8, 9, /* SBUS slot 1 */
+ 0, 1, 2, 7, 5, 7, 8, 9, /* SBUS slot 2 */
+ 0, 1, 2, 7, 5, 7, 8, 9, /* SBUS slot 3 */
3, /* Onboard SCSI */
5, /* Onboard Ethernet */
/*XXX*/ 8, /* Onboard BPP */
@@ -112,7 +158,7 @@ unsigned char ino_to_pil[] = {
*/
#define offset(x) ((unsigned long)(&(((struct sysio_regs *)0)->x)))
#define bogon ((unsigned long) -1)
-static unsigned long irq_offsets[] = {
+static unsigned long sysio_irq_offsets[] = {
/* SBUS Slot 0 --> 3, level 1 --> 7 */
offset(imap_slot0),offset(imap_slot0),offset(imap_slot0),offset(imap_slot0),
offset(imap_slot0),offset(imap_slot0),offset(imap_slot0),offset(imap_slot0),
@@ -134,29 +180,29 @@ offset(imap_pmgmt),
#undef bogon
-#define NUM_IRQ_ENTRIES (sizeof(irq_offsets) / sizeof(irq_offsets[0]))
+#define NUM_SYSIO_OFFSETS (sizeof(sysio_irq_offsets) / sizeof(sysio_irq_offsets[0]))
-/* Convert an "interrupts" property IRQ level to an SBUS/SYSIO
- * Interrupt Mapping register pointer, or NULL if none exists.
+/* XXX Old compatability cruft, get rid of me when all drivers have been
+ * XXX converted to dcookie registry calls... -DaveM
*/
-static unsigned int *irq_to_imap(unsigned int irq)
+static unsigned int *sysio_irq_to_imap(unsigned int irq)
{
unsigned long offset;
struct sysio_regs *sregs;
if((irq == 14) ||
- (irq >= NUM_IRQ_ENTRIES) ||
- ((offset = irq_offsets[irq]) == ((unsigned long)-1)))
+ (irq >= NUM_SYSIO_OFFSETS) ||
+ ((offset = sysio_irq_offsets[irq]) == ((unsigned long)-1)))
return NULL;
sregs = SBus_chain->iommu->sysio_regs;
offset += ((unsigned long) sregs);
- return ((unsigned int *)offset) + 1;
+ return ((unsigned int *)offset);
}
/* Convert Interrupt Mapping register pointer to assosciated
- * Interrupt Clear register pointer.
+ * Interrupt Clear register pointer, SYSIO specific version.
*/
-static unsigned int *imap_to_iclr(unsigned int *imap)
+static unsigned int *sysio_imap_to_iclr(unsigned int *imap)
{
unsigned long diff;
@@ -166,32 +212,68 @@ static unsigned int *imap_to_iclr(unsigned int *imap)
#undef offset
-/* For non-SBUS IRQ's we do nothing, else we must enable them in the
- * appropriate SYSIO interrupt map registers.
+#ifdef CONFIG_PCI
+/* PCI PSYCHO INO number to Sparc PIL level. */
+unsigned char psycho_ino_to_pil[] = {
+ 7, 5, 5, 2, /* PCI A slot 0 Int A, B, C, D */
+ 7, 5, 5, 2, /* PCI A slot 1 Int A, B, C, D */
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 6, 4, 3, 1, /* PCI B slot 0 Int A, B, C, D */
+ 6, 4, 3, 1, /* PCI B slot 1 Int A, B, C, D */
+ 6, 4, 3, 1, /* PCI B slot 2 Int A, B, C, D */
+ 6, 4, 3, 1, /* PCI B slot 3 Int A, B, C, D */
+ 3, /* SCSI */
+ 5, /* Ethernet */
+ 8, /* Parallel Port */
+ 13, /* Audio Record */
+ 14, /* Audio Playback */
+ 15, /* PowerFail */
+ 12, /* Keyboard/Mouse/Serial */
+ 11, /* Floppy */
+ 2, /* Spare Hardware */
+ 12, /* Keyboard */
+ 4, /* Mouse */
+ 12, /* Serial */
+ 10, /* Timer 0 */
+ 11, /* Timer 1 */
+ 15, /* Uncorrectable ECC */
+ 15, /* Correctable ECC */
+ 15, /* PCI Bus A Error */
+ 15, /* PCI Bus B Error */
+ 1, /* Power Management */
+};
+
+/* INO number to IMAP register offset for PSYCHO external IRQ's.
*/
-void enable_irq(unsigned int irq)
+#define psycho_offset(x) ((unsigned long)(&(((struct psycho_regs *)0)->x)))
+
+#define psycho_imap_offset(ino) \
+ ((ino & 0x20) ? (psycho_offset(imap_scsi) + (((ino) & 0x1f) << 3)) : \
+ (psycho_offset(imap_a_slot0) + (((ino) & 0x3c) << 1)))
+
+#define psycho_iclr_offset(ino) \
+ ((ino & 0x20) ? (psycho_offset(iclr_scsi) + (((ino) & 0x1f) << 3)) : \
+ (psycho_offset(iclr_a_slot0[0]) + (((ino) & 0x1f) << 3)))
+
+#endif
+
+/* Now these are always passed a true fully specified sun4u INO. */
+void enable_irq(unsigned int ino)
{
+ struct ino_bucket *bucket;
unsigned long tid;
unsigned int *imap;
- /* If this is for the tick interrupt, just ignore, note
- * that this is the one and only locally generated interrupt
- * source, all others come from external sources (essentially
- * any UPA device which is an interruptor). (actually, on
- * second thought Ultra can generate local interrupts for
- * async memory errors and we may setup handlers for those
- * at some point as well)
- *
- * XXX See commentary below in request_irq() this assumption
- * XXX is broken and needs to be fixed.
- */
- if(irq == 14)
+#ifdef CONFIG_PCI
+ if(PCI_IRQ_P(ino))
+ ino &= (PCI_IRQ_IGN | PCI_IRQ_INO);
+#endif
+ bucket = ino_lookup(ino);
+ if(!bucket)
return;
- /* Check for bogons. */
- imap = irq_to_imap(irq);
- if(imap == NULL)
- goto do_the_stb_watoosi;
+ imap = bucket->imap;
/* 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));
@@ -202,28 +284,27 @@ void enable_irq(unsigned int irq)
* Register, the hardware just mirrors that value here.
* However for Graphics and UPA Slave devices the full
* SYSIO_IMAP_INR field can be set by the programmer here.
- * (XXX we will have to handle those for FFB etc. XXX)
+ *
+ * Things like FFB can now be handled via the dcookie mechanism.
*/
*imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID);
- return;
-
-do_the_stb_watoosi:
- printk("Cannot enable irq(%d), doing the \"STB Watoosi\" instead.", irq);
- panic("Trying to enable bogon IRQ");
}
-void disable_irq(unsigned int irq)
+/* This now gets passed true ino's as well. */
+void disable_irq(unsigned int ino)
{
+ struct ino_bucket *bucket;
unsigned int *imap;
- /* XXX Grrr, I know this is broken... */
- if(irq == 14)
+#ifdef CONFIG_PCI
+ if(PCI_IRQ_P(ino))
+ ino &= (PCI_IRQ_IGN | PCI_IRQ_INO);
+#endif
+ bucket = ino_lookup(ino);
+ if(!bucket)
return;
- /* Check for bogons. */
- imap = irq_to_imap(irq);
- if(imap == NULL)
- goto do_the_stb_watoosi;
+ imap = bucket->imap;
/* NOTE: We do not want to futz with the IRQ clear registers
* and move the state to IDLE, the SCSI code does call
@@ -231,34 +312,254 @@ void disable_irq(unsigned int irq)
* SCSI adapter driver code. Thus we'd lose interrupts.
*/
*imap &= ~(SYSIO_IMAP_VALID);
- return;
+}
+
+static void get_irq_translations(int *cpu_irq, int *ivindex_fixup,
+ unsigned int **imap, unsigned int **iclr,
+ void *busp, unsigned long flags,
+ unsigned int irq)
+{
+ if(*cpu_irq != -1 && *imap != NULL && *iclr != NULL)
+ return;
+
+ if(*cpu_irq != -1 || *imap != NULL || *iclr != NULL || busp == NULL) {
+ printk("get_irq_translations: Partial specification, this is bad.\n");
+ printk("get_irq_translations: cpu_irq[%d] imap[%p] iclr[%p] busp[%p]\n",
+ *cpu_irq, *imap, *iclr, busp);
+ panic("Bad IRQ translations...");
+ }
+
+ if(SA_BUS(flags) == SA_SBUS) {
+ struct linux_sbus *sbusp = busp;
+ struct sysio_regs *sregs = sbusp->iommu->sysio_regs;
+ unsigned long offset;
-do_the_stb_watoosi:
- printk("Cannot disable irq(%d), doing the \"STB Watoosi\" instead.", irq);
- panic("Trying to enable bogon IRQ");
+ *cpu_irq = sysio_ino_to_pil[irq];
+ if(*cpu_irq == 0) {
+ printk("get_irq_translations: Bad SYSIO INO[%x]\n", irq);
+ panic("Bad SYSIO IRQ translations...");
+ }
+ offset = sysio_irq_offsets[irq];
+ if(offset == ((unsigned long)-1)) {
+ printk("get_irq_translations: Bad SYSIO INO[%x] cpu[%d]\n",
+ irq, *cpu_irq);
+ panic("BAD SYSIO IRQ offset...");
+ }
+ offset += ((unsigned long)sregs);
+ *imap = ((unsigned int *)offset);
+
+ /* SYSIO inconsistancy. For external SLOTS, we have to select
+ * the right ICLR register based upon the lower SBUS irq level
+ * bits.
+ */
+ if(irq >= 0x20) {
+ *iclr = sysio_imap_to_iclr(*imap);
+ } else {
+ unsigned long iclraddr;
+ int sbus_slot = (irq & 0x18)>>3;
+ int sbus_level = irq & 0x7;
+
+ switch(sbus_slot) {
+ case 0:
+ *iclr = &sregs->iclr_slot0;
+ break;
+ case 1:
+ *iclr = &sregs->iclr_slot1;
+ break;
+ case 2:
+ *iclr = &sregs->iclr_slot2;
+ break;
+ case 3:
+ *iclr = &sregs->iclr_slot3;
+ break;
+ };
+
+ iclraddr = (unsigned long) *iclr;
+ iclraddr += ((sbus_level - 1) * 8);
+ *iclr = (unsigned int *) iclraddr;
+
+#if 0 /* DEBUGGING */
+ printk("SYSIO_FIXUP: slot[%x] level[%x] iclr[%p] ",
+ sbus_slot, sbus_level, *iclr);
+#endif
+
+ /* Also, make sure this is accounted for in ivindex
+ * computations done by the caller.
+ */
+ *ivindex_fixup = sbus_level;
+ }
+ return;
+ }
+#ifdef CONFIG_PCI
+ if(SA_BUS(flags) == SA_PCI) {
+ struct pci_bus *pbusp = busp;
+ struct linux_pbm_info *pbm = pbusp->sysdata;
+ struct psycho_regs *pregs = pbm->parent->psycho_regs;
+ unsigned long offset;
+
+ *cpu_irq = psycho_ino_to_pil[irq & 0x3f];
+ if(*cpu_irq == 0) {
+ printk("get_irq_translations: Bad PSYCHO INO[%x]\n", irq);
+ panic("Bad PSYCHO IRQ translations...");
+ }
+ offset = psycho_imap_offset(irq);
+ if(offset == ((unsigned long)-1)) {
+ printk("get_irq_translations: Bad PSYCHO INO[%x] cpu[%d]\n",
+ irq, *cpu_irq);
+ panic("Bad PSYCHO IRQ offset...");
+ }
+ offset += ((unsigned long)pregs);
+ *imap = ((unsigned int *)offset) + 1;
+ *iclr = (unsigned int *)
+ (((unsigned long)pregs) + psycho_imap_offset(irq));
+ return;
+ }
+#endif
+#if 0 /* XXX More to do before we can use this. -DaveM */
+ if(SA_BUS(flags) == SA_FHC) {
+ struct fhc_bus *fbusp = busp;
+ struct fhc_regs *fregs = fbusp->regs;
+ unsigned long offset;
+
+ *cpu_irq = fhc_ino_to_pil[irq];
+ if(*cpu_irq == 0) {
+ printk("get_irq_translations: Bad FHC INO[%x]\n", irq);
+ panic("Bad FHC IRQ translations...");
+ }
+ offset = fhc_irq_offset[*cpu_irq];
+ if(offset == ((unsigned long)-1)) {
+ printk("get_irq_translations: Bad FHC INO[%x] cpu[%d]\n",
+ irq, *cpu_irq);
+ panic("Bad FHC IRQ offset...");
+ }
+ offset += ((unsigned long)pregs);
+ *imap = (((unsigned int *)offset)+1);
+ *iclr = fhc_imap_to_iclr(*imap);
+ return;
+ }
+#endif
+ printk("get_irq_translations: IRQ register for unknown bus type.\n");
+ printk("get_irq_translations: BUS[%lx] IRQ[%x]\n",
+ SA_BUS(flags), irq);
+ panic("Bad IRQ bus type...");
+}
+
+#ifdef CONFIG_PCI
+static void pci_irq_frobnicate(int *cpu_irq, int *ivindex_fixup,
+ unsigned int **imap, unsigned int **iclr,
+ unsigned int irq)
+{
+ struct linux_psycho *psycho = psycho_root;
+ struct psycho_regs *pregs = psycho->psycho_regs;
+ unsigned long addr, imoff;
+
+ addr = (unsigned long) &pregs->imap_a_slot0;
+ imoff = (irq & PCI_IRQ_IMAP_OFF) >> PCI_IRQ_IMAP_OFF_SHFT;
+ addr = addr + imoff;
+
+ *imap = ((unsigned int *)addr) + 1;
+
+ addr = (unsigned long) pregs;
+ addr += psycho_iclr_offset(irq & (PCI_IRQ_INO));
+ *iclr = ((unsigned int *)addr) + 1;
+
+ *cpu_irq = psycho_ino_to_pil[irq & (PCI_IRQ_INO)];
+ if(*cpu_irq == 0) {
+ printk("get_irq_translations: BAD PSYCHO INO[%x]\n", irq);
+ panic("Bad PSYCHO IRQ frobnication...");
+ }
+
+ /* IVINDEX fixup only needed for PCI slot irq lines. */
+ if(!(irq & 0x20))
+ *ivindex_fixup = irq & 0x03;
+}
+#endif
+
+/* Once added, they are never removed. */
+static struct ino_bucket *add_ino_hash(unsigned int ivindex,
+ unsigned int *imap, unsigned int *iclr,
+ unsigned long flags)
+{
+ struct ino_bucket *new = NULL, **hashp;
+ unsigned int hash = (ivindex & (INO_HASHSZ - 1));
+
+ new = __ino_lookup(hash, ivindex);
+ if(new)
+ return new;
+ if(flags & SA_STATIC_ALLOC) {
+ if(static_ino_bucket_count < NUM_INO_STATIC)
+ new = &static_ino_buckets[static_ino_bucket_count++];
+ else
+ printk("Request for ino bucket SA_STATIC_ALLOC failed "
+ "using kmalloc\n");
+ }
+ if(new == NULL)
+ new = kmalloc(sizeof(struct ino_bucket), GFP_KERNEL);
+ if(new) {
+ hashp = &ino_hash[hash];
+ new->imap = imap;
+ new->iclr = iclr;
+ new->ino = ivindex;
+ new->next = *hashp;
+ *hashp = new;
+ }
+ return new;
}
int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
- unsigned long irqflags, const char *name, void *dev_cookie)
+ unsigned long irqflags, const char *name, void *dev_id)
{
struct irqaction *action, *tmp = NULL;
+ struct devid_cookie *dcookie = NULL;
+ struct ino_bucket *bucket = NULL;
unsigned long flags;
- unsigned int cpu_irq, *imap, *iclr;
+ unsigned int *imap, *iclr;
+ void *bus_id = NULL;
+ int ivindex, ivindex_fixup, cpu_irq = -1;
- /* XXX This really is not the way to do it, the "right way"
- * XXX is to have drivers set SA_SBUS or something like that
- * XXX in irqflags and we base our decision here on whether
- * XXX that flag bit is set or not.
- */
- if(irq == 14)
- cpu_irq = irq;
- else
- cpu_irq = ino_to_pil[irq];
-
if(!handler)
return -EINVAL;
- imap = irq_to_imap(irq);
+ imap = iclr = NULL;
+
+ ivindex_fixup = 0;
+#ifdef CONFIG_PCI
+ if(PCI_IRQ_P(irq)) {
+ pci_irq_frobnicate(&cpu_irq, &ivindex_fixup, &imap, &iclr, irq);
+ } else
+#endif
+ if(irqflags & SA_DCOOKIE) {
+ if(!dev_id) {
+ printk("request_irq: SA_DCOOKIE but dev_id is NULL!\n");
+ panic("Bogus irq registry.");
+ }
+ dcookie = dev_id;
+ dev_id = dcookie->real_dev_id;
+ cpu_irq = dcookie->pil;
+ imap = dcookie->imap;
+ iclr = dcookie->iclr;
+ bus_id = dcookie->bus_cookie;
+ get_irq_translations(&cpu_irq, &ivindex_fixup, &imap,
+ &iclr, bus_id, irqflags, irq);
+ } else {
+ /* XXX NOTE: This code is maintained for compatability until I can
+ * XXX verify that all drivers sparc64 will use are updated
+ * XXX to use the new IRQ registry dcookie interface. -DaveM
+ */
+ if(irq == 14)
+ cpu_irq = irq;
+ else
+ cpu_irq = sysio_ino_to_pil[irq];
+ imap = sysio_irq_to_imap(irq);
+ if(!imap) {
+ printk("request_irq: BAD, null imap for old style "
+ "irq registry IRQ[%x].\n", irq);
+ panic("Bad IRQ registery...");
+ }
+ iclr = sysio_imap_to_iclr(imap);
+ }
+ ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO));
+ ivindex += ivindex_fixup;
action = *(cpu_irq + irq_action);
if(action) {
@@ -297,52 +598,67 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *)
return -ENOMEM;
}
- if(imap) {
- int ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO));
+ bucket = add_ino_hash(ivindex, imap, iclr, irqflags);
+ if(!bucket) {
+ kfree(action);
+ restore_flags(flags);
+ return -ENOMEM;
+ }
- ivector_to_mask[ivindex] = (1<<cpu_irq);
- iclr = imap_to_iclr(imap);
- action->mask = (unsigned long) iclr;
- irqflags |= SA_SYSIO_MASKED;
- } else {
- action->mask = 0;
+ ivector_to_mask[ivindex] = (1 << cpu_irq);
+
+ if(dcookie) {
+ dcookie->ret_ino = ivindex;
+ dcookie->ret_pil = cpu_irq;
}
+ action->mask = (unsigned long) bucket;
action->handler = handler;
- action->flags = irqflags;
+ action->flags = irqflags | SA_IMAP_MASKED;
action->name = name;
action->next = NULL;
- action->dev_id = dev_cookie;
+ action->dev_id = dev_id;
if(tmp)
tmp->next = action;
else
*(cpu_irq + irq_action) = action;
- enable_irq(irq);
+ enable_irq(ivindex);
restore_flags(flags);
+#ifdef __SMP__
+ if(irqs_have_been_distributed)
+ distribute_irqs();
+#endif
return 0;
}
-void free_irq(unsigned int irq, void *dev_cookie)
+void free_irq(unsigned int irq, void *dev_id)
{
struct irqaction *action;
struct irqaction *tmp = NULL;
unsigned long flags;
unsigned int cpu_irq;
+ int ivindex = -1;
- if(irq == 14)
+ if(irq == 14) {
cpu_irq = irq;
- else
- cpu_irq = ino_to_pil[irq];
+ } else {
+#ifdef CONFIG_PCI
+ if(PCI_IRQ_P(irq))
+ cpu_irq = psycho_ino_to_pil[irq & PCI_IRQ_INO];
+ else
+#endif
+ cpu_irq = sysio_ino_to_pil[irq];
+ }
action = *(cpu_irq + irq_action);
if(!action->handler) {
printk("Freeing free IRQ %d\n", irq);
return;
}
- if(dev_cookie) {
+ if(dev_id) {
for( ; action; action = action->next) {
- if(action->dev_id == dev_cookie)
+ if(action->dev_id == dev_id)
break;
tmp = action;
}
@@ -351,7 +667,7 @@ void free_irq(unsigned int irq, void *dev_cookie)
return;
}
} else if(action->flags & SA_SHIRQ) {
- printk("Trying to free shared IRQ %d with NULL device cookie\n", irq);
+ printk("Trying to free shared IRQ %d with NULL device ID\n", irq);
return;
}
@@ -367,29 +683,37 @@ void free_irq(unsigned int irq, void *dev_cookie)
else
*(cpu_irq + irq_action) = action->next;
- if(action->flags & SA_SYSIO_MASKED) {
- unsigned int *imap = irq_to_imap(irq);
- if(imap != NULL)
- ivector_to_mask[*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)] = 0;
+ if(action->flags & SA_IMAP_MASKED) {
+ struct ino_bucket *bucket = (struct ino_bucket *)action->mask;
+ unsigned int *imap = bucket->imap;
+
+ if(imap != NULL) {
+ ivindex = bucket->ino;
+ ivector_to_mask[ivindex] = 0;
+ }
else
printk("free_irq: WHeee, SYSIO_MASKED yet no imap reg.\n");
}
kfree(action);
- if(!*(cpu_irq + irq_action))
- disable_irq(irq);
+ if(ivindex != -1)
+ disable_irq(ivindex);
restore_flags(flags);
}
-/* Per-processor IRQ locking depth, both SMP and non-SMP code use this. */
-unsigned int local_irq_count[NR_CPUS];
+/* Only uniprocessor needs this IRQ locking depth, on SMP it lives in the per-cpu
+ * structure for cache reasons.
+ */
+#ifndef __SMP__
+unsigned int local_irq_count;
+#endif
#ifndef __SMP__
int __sparc64_bh_counter = 0;
-#define irq_enter(cpu, irq) (local_irq_count[cpu]++)
-#define irq_exit(cpu, irq) (local_irq_count[cpu]--)
+#define irq_enter(cpu, irq) (local_irq_count++)
+#define irq_exit(cpu, irq) (local_irq_count--)
#else
@@ -407,18 +731,31 @@ spinlock_t global_bh_lock = SPIN_LOCK_UNLOCKED;
/* Global IRQ locking depth. */
atomic_t global_irq_count = ATOMIC_INIT(0);
-static inline void wait_on_irq(int cpu)
+static unsigned long previous_irqholder;
+
+#undef INIT_STUCK
+#define INIT_STUCK 100000000
+
+#undef STUCK
+#define STUCK \
+if (!--stuck) {printk("wait_on_irq CPU#%d stuck at %08lx, waiting for %08lx (local=%d, global=%d)\n", cpu, where, previous_irqholder, local_count, atomic_read(&global_irq_count)); stuck = INIT_STUCK; }
+
+static inline void wait_on_irq(int cpu, unsigned long where)
{
- int local_count = local_irq_count[cpu];
+ int stuck = INIT_STUCK;
+ int local_count = local_irq_count;
while(local_count != atomic_read(&global_irq_count)) {
atomic_sub(local_count, &global_irq_count);
spin_unlock(&global_irq_lock);
for(;;) {
+ STUCK;
+ membar("#StoreLoad | #LoadLoad");
if (atomic_read(&global_irq_count))
continue;
- if (*((unsigned char *)&global_irq_lock))
+ if (*((volatile unsigned char *)&global_irq_lock))
continue;
+ membar("#LoadLoad | #LoadStore");
if (spin_trylock(&global_irq_lock))
break;
}
@@ -426,25 +763,41 @@ static inline void wait_on_irq(int cpu)
}
}
-static inline void get_irqlock(int cpu)
+#undef INIT_STUCK
+#define INIT_STUCK 10000000
+
+#undef STUCK
+#define STUCK \
+if (!--stuck) {printk("get_irqlock stuck at %08lx, waiting for %08lx\n", where, previous_irqholder); stuck = INIT_STUCK;}
+
+static inline void get_irqlock(int cpu, unsigned long where)
{
+ int stuck = INIT_STUCK;
+
if (!spin_trylock(&global_irq_lock)) {
+ membar("#StoreLoad | #LoadLoad");
if ((unsigned char) cpu == global_irq_holder)
return;
do {
- barrier();
+ do {
+ STUCK;
+ membar("#LoadLoad");
+ } while(*((volatile unsigned char *)&global_irq_lock));
} while (!spin_trylock(&global_irq_lock));
}
- wait_on_irq(cpu);
+ wait_on_irq(cpu, where);
global_irq_holder = cpu;
+ previous_irqholder = where;
}
void __global_cli(void)
{
int cpu = smp_processor_id();
+ unsigned long where;
+ __asm__ __volatile__("mov %%i7, %0" : "=r" (where));
__cli();
- get_irqlock(cpu);
+ get_irqlock(cpu, where);
}
void __global_sti(void)
@@ -453,11 +806,6 @@ void __global_sti(void)
__sti();
}
-unsigned long __global_save_flags(void)
-{
- return global_irq_holder == (unsigned char) smp_processor_id();
-}
-
void __global_restore_flags(unsigned long flags)
{
if (flags & 1) {
@@ -472,15 +820,24 @@ void __global_restore_flags(unsigned long flags)
}
}
+#undef INIT_STUCK
+#define INIT_STUCK 200000000
+
+#undef STUCK
+#define STUCK \
+if (!--stuck) {printk("irq_enter stuck (irq=%d, cpu=%d, global=%d)\n",irq,cpu,global_irq_holder); stuck = INIT_STUCK;}
+
void irq_enter(int cpu, int irq)
{
+ int stuck = INIT_STUCK;
+
hardirq_enter(cpu);
- barrier();
- while (*((unsigned char *)&global_irq_lock)) {
+ while (*((volatile unsigned char *)&global_irq_lock)) {
if ((unsigned char) cpu == global_irq_holder)
printk("irq_enter: Frosted Lucky Charms, "
"they're magically delicious!\n");
- barrier();
+ STUCK;
+ membar("#LoadLoad");
}
}
@@ -492,8 +849,7 @@ void irq_exit(int cpu, int irq)
void synchronize_irq(void)
{
- int cpu = smp_processor_id();
- int local_count = local_irq_count[cpu];
+ int local_count = local_irq_count;
unsigned long flags;
if (local_count != atomic_read(&global_irq_count)) {
@@ -506,9 +862,13 @@ void synchronize_irq(void)
void report_spurious_ivec(struct pt_regs *regs)
{
- printk("IVEC: Spurious interrupt vector received at (%016lx)\n",
- regs->tpc);
- return;
+ extern unsigned long ivec_spurious_cookie;
+ static int times = 0;
+
+ printk("IVEC: Spurious interrupt vector (%016lx) received at (%016lx)\n",
+ ivec_spurious_cookie, regs->tpc);
+ if(times++ > 1)
+ prom_halt();
}
void unexpected_irq(int irq, void *dev_cookie, struct pt_regs *regs)
@@ -547,13 +907,28 @@ void handler_irq(int irq, struct pt_regs *regs)
irq_enter(cpu, irq);
action = *(irq + irq_action);
kstat.interrupts[irq]++;
- do {
- if(!action || !action->handler)
- unexpected_irq(irq, 0, regs);
- action->handler(irq, action->dev_id, regs);
- if(action->flags & SA_SYSIO_MASKED)
- *((unsigned int *)action->mask) = SYSIO_ICLR_IDLE;
- } while((action = action->next) != NULL);
+ if(!action) {
+ unexpected_irq(irq, 0, regs);
+ } else {
+ do {
+ struct ino_bucket *bucket = NULL;
+ unsigned int ino = 0;
+
+ if(action->flags & SA_IMAP_MASKED) {
+ bucket = (struct ino_bucket *)action->mask;
+
+ ino = bucket->ino;
+ if(!(ivector_to_mask[ino] & 0x80000000))
+ continue;
+ }
+
+ action->handler(irq, action->dev_id, regs);
+ if(bucket) {
+ ivector_to_mask[ino] &= ~(0x80000000);
+ *(bucket->iclr) = SYSIO_ICLR_IDLE;
+ }
+ } while((action = action->next) != NULL);
+ }
irq_exit(cpu, irq);
}
@@ -567,7 +942,7 @@ void sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs)
irq_enter(cpu, irq);
floppy_interrupt(irq, dev_cookie, regs);
- if(action->flags & SA_SYSIO_MASKED)
+ if(action->flags & SA_IMAP_MASKED)
*((unsigned int *)action->mask) = SYSIO_ICLR_IDLE;
irq_exit(cpu, irq);
}
@@ -595,7 +970,7 @@ static void install_fast_irq(unsigned int cpu_irq,
insns[0] = SPARC_BRANCH(((unsigned long) handler),
((unsigned long)&insns[0]));
insns[1] = SPARC_NOP;
- __asm__ __volatile__("flush %0" : : "r" (ttent));
+ __asm__ __volatile__("membar #StoreStore; flush %0" : : "r" (ttent));
}
int request_fast_irq(unsigned int irq,
@@ -605,6 +980,7 @@ int request_fast_irq(unsigned int irq,
struct irqaction *action;
unsigned long flags;
unsigned int cpu_irq, *imap, *iclr;
+ int ivindex = -1;
/* XXX This really is not the way to do it, the "right way"
* XXX is to have drivers set SA_SBUS or something like that
@@ -616,11 +992,11 @@ int request_fast_irq(unsigned int irq,
*/
if(irq == 14)
return -EINVAL;
- cpu_irq = ino_to_pil[irq];
+ cpu_irq = sysio_ino_to_pil[irq];
if(!handler)
return -EINVAL;
- imap = irq_to_imap(irq);
+ imap = sysio_irq_to_imap(irq);
action = *(cpu_irq + irq_action);
if(action) {
if(action->flags & SA_SHIRQ)
@@ -648,12 +1024,12 @@ int request_fast_irq(unsigned int irq,
install_fast_irq(cpu_irq, handler);
if(imap) {
- int ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO));
-
+ ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO));
ivector_to_mask[ivindex] = (1 << cpu_irq);
- iclr = imap_to_iclr(imap);
+ iclr = sysio_imap_to_iclr(imap);
action->mask = (unsigned long) iclr;
- irqflags |= SA_SYSIO_MASKED;
+ irqflags |= SA_IMAP_MASKED;
+ add_ino_hash(ivindex, imap, iclr, irqflags);
} else
action->mask = 0;
@@ -665,7 +1041,9 @@ int request_fast_irq(unsigned int irq,
*(cpu_irq + irq_action) = action;
- enable_irq(irq);
+ if(ivindex != -1)
+ enable_irq(ivindex);
+
restore_flags(flags);
return 0;
}
@@ -675,31 +1053,27 @@ int request_fast_irq(unsigned int irq,
*/
unsigned long probe_irq_on(void)
{
- return 0;
+ return 0;
}
int probe_irq_off(unsigned long mask)
{
- return 0;
+ return 0;
}
struct sun5_timer *linux_timers = NULL;
-/* This is called from sbus_init() to get the jiffies timer going.
- * We need to call this after there exists a valid SBus_chain so
- * that the IMAP/ICLR registers can be accessed.
- *
- * XXX That is because the whole startup sequence is broken. I will
- * XXX fix it all up very soon. -DaveM
- */
+/* This is gets the master level10 timer going. */
void init_timers(void (*cfunc)(int, void *, struct pt_regs *))
{
struct linux_prom64_registers pregs[3];
+ struct devid_cookie dcookie;
+ unsigned int *imap, *iclr;
u32 pirqs[2];
int node, err;
node = prom_finddevice("/counter-timer");
- if(node == 0) {
+ if(node == 0 || node == -1) {
prom_printf("init_timers: Cannot find counter-timer PROM node.\n");
prom_halt();
}
@@ -715,13 +1089,22 @@ void init_timers(void (*cfunc)(int, void *, struct pt_regs *))
prom_halt();
}
linux_timers = (struct sun5_timer *) __va(pregs[0].phys_addr);
+ iclr = (((unsigned int *)__va(pregs[1].phys_addr))+1);
+ imap = (((unsigned int *)__va(pregs[2].phys_addr))+1);
/* Shut it up first. */
linux_timers->limit0 = 0;
/* Register IRQ handler. */
- err = request_irq(pirqs[0] & 0x3f, /* XXX Fix this for big Enterprise XXX */
- cfunc, (SA_INTERRUPT | SA_STATIC_ALLOC), "timer", NULL);
+ dcookie.real_dev_id = NULL;
+ dcookie.imap = imap;
+ dcookie.iclr = iclr;
+ dcookie.pil = 10;
+ dcookie.bus_cookie = NULL;
+
+ err = request_irq(pirqs[0], cfunc,
+ (SA_DCOOKIE | SA_INTERRUPT | SA_STATIC_ALLOC),
+ "timer", &dcookie);
if(err) {
prom_printf("Serious problem, cannot register timer interrupt\n");
@@ -825,6 +1208,52 @@ void enable_prom_timer(void)
prom_timers->count0 = 0;
}
+#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.
+ */
+void distribute_irqs(void)
+{
+ unsigned long flags;
+ int cpu, level;
+
+ printk("SMP: redistributing interrupts...\n");
+ 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 = bucket->imap;
+ unsigned int val;
+ unsigned long tid = linux_cpus[cpu].mid << 9;
+
+ val = *imap;
+ *imap = SYSIO_IMAP_VALID | (tid & SYSIO_IMAP_TID);
+
+ printk("SMP: Redirecting IGN[%x] INO[%x] "
+ "to cpu %d [%s]\n",
+ (val & SYSIO_IMAP_IGN) >> 6,
+ (val & SYSIO_IMAP_INO), cpu,
+ p->name);
+
+ cpu += 1;
+ while(!(cpu_present_map & (1UL << cpu))) {
+ cpu += 1;
+ if(cpu >= smp_num_cpus)
+ cpu = 0;
+ }
+ }
+ p = p->next;
+ }
+ }
+ restore_flags(flags);
+ irqs_have_been_distributed = 1;
+}
+#endif
+
__initfunc(void init_IRQ(void))
{
int i;