patch-2.1.97 linux/arch/ppc/kernel/irq.c
Next file: linux/arch/ppc/kernel/mbx_pci.c
Previous file: linux/arch/ppc/kernel/idle.c
Back to the patch index
Back to the overall index
- Lines: 562
- Date:
Tue Apr 14 17:33:57 1998
- Orig file:
v2.1.96/linux/arch/ppc/kernel/irq.c
- Orig date:
Thu Feb 12 20:56:04 1998
diff -u --recursive --new-file v2.1.96/linux/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c
@@ -14,6 +14,14 @@
* instead of just grabbing them. Thus setups with different IRQ numbers
* shouldn't result in any weird surprises, and installing new handlers
* should be easier.
+ *
+ * The MPC8xx has an interrupt mask in the SIU. If a bit is set, the
+ * interrupt is _enabled_. As expected, IRQ0 is bit 0 in the 32-bit
+ * mask register (of which only 16 are defined), hence the weird shifting
+ * and compliment of the cached_irq_mask. I want to be able to stuff
+ * this right into the SIU SMASK register.
+ * Many of the prep/chrp functions are conditional compiled on CONFIG_8xx
+ * to reduce code space and undefined function references.
*/
@@ -30,30 +38,41 @@
#include <linux/malloc.h>
#include <linux/openpic.h>
#include <linux/pci.h>
-#include <linux/bios32.h>
#include <linux/openpic.h>
#include <asm/hydra.h>
#include <asm/system.h>
#include <asm/io.h>
+#include <asm/pgtable.h>
#include <asm/irq.h>
#include <asm/bitops.h>
#include <asm/gg2.h>
+#include <asm/cache.h>
+#ifdef CONFIG_8xx
+#include <asm/8xx_immap.h>
+#include <asm/mbx.h>
+#endif
#undef SHOW_IRQ
unsigned lost_interrupts = 0;
unsigned int local_irq_count[NR_CPUS];
-static struct irqaction irq_action[NR_IRQS];
+static struct irqaction *irq_action[NR_IRQS];
static int spurious_interrupts = 0;
+#ifndef CONFIG_8xx
static unsigned int cached_irq_mask = 0xffffffff;
+#else
+static unsigned int cached_irq_mask = 0xffffffff;
+#endif
static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
-spinlock_t irq_controller_lock;
+/*spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED;*/
#ifdef __SMP__
atomic_t __ppc_bh_counter = ATOMIC_INIT(0);
#else
int __ppc_bh_counter = 0;
#endif
+static volatile unsigned char *gg2_int_ack_special;
+extern volatile unsigned long ipi_count;
#define cached_21 (((char *)(&cached_irq_mask))[3])
#define cached_A1 (((char *)(&cached_irq_mask))[2])
@@ -61,9 +80,19 @@
/*
* These are set to the appropriate functions by init_IRQ()
*/
+#ifndef CONFIG_8xx
void (*mask_and_ack_irq)(int irq_nr);
void (*mask_irq)(unsigned int irq_nr);
void (*unmask_irq)(unsigned int irq_nr);
+#else /* CONFIG_8xx */
+/* init_IRQ() happens too late for the MBX because we initialize the
+ * CPM early and it calls request_irq() before we have these function
+ * pointers initialized.
+ */
+#define mask_and_ack_irq(irq) mbx_mask_irq(irq)
+#define mask_irq(irq) mbx_mask_irq(irq)
+#define unmask_irq(irq) mbx_unmask_irq(irq)
+#endif /* CONFIG_8xx */
/* prep */
@@ -79,9 +108,46 @@
#define PMAC_IRQ_MASK (~ld_le32(IRQ_ENABLE))
+
+/* nasty hack for shared irq's since we need to do kmalloc calls but
+ * can't very very early in the boot when we need to do a request irq.
+ * this needs to be removed.
+ * -- Cort
+ */
+static char cache_bitmask = 0;
+static struct irqaction malloc_cache[4];
+extern int mem_init_done;
+
+void *irq_kmalloc(size_t size, int pri)
+{
+ unsigned int i;
+ if ( mem_init_done )
+ return kmalloc(size,pri);
+ for ( i = 0; i <= 3 ; i++ )
+ if ( ! ( cache_bitmask & (1<<i) ) )
+ {
+ cache_bitmask |= (1<<i);
+ return (void *)(&malloc_cache[i]);
+ }
+ return 0;
+}
+
+void irq_kfree(void *ptr)
+{
+ unsigned int i;
+ for ( i = 0 ; i <= 3 ; i++ )
+ if ( ptr == &malloc_cache[i] )
+ {
+ cache_bitmask &= ~(1<<i);
+ return;
+ }
+ kfree(ptr);
+}
+
+#ifndef CONFIG_8xx
void i8259_mask_and_ack_irq(int irq_nr)
{
- spin_lock(&irq_controller_lock);
+ /* spin_lock(&irq_controller_lock);*/
cached_irq_mask |= 1 << irq_nr;
if (irq_nr > 7) {
inb(0xA1); /* DUMMY */
@@ -96,20 +162,22 @@
outb(0x60|irq_nr,0x20); /* specific eoi */
}
- spin_unlock(&irq_controller_lock);
+ /* spin_unlock(&irq_controller_lock);*/
}
void pmac_mask_and_ack_irq(int irq_nr)
{
unsigned long bit = 1UL << irq_nr;
- spin_lock(&irq_controller_lock);
+ /* spin_lock(&irq_controller_lock);*/
cached_irq_mask |= bit;
lost_interrupts &= ~bit;
out_le32(IRQ_ACK, bit);
out_le32(IRQ_ENABLE, ~cached_irq_mask);
out_le32(IRQ_ACK, bit);
- spin_unlock(&irq_controller_lock);
+ /* spin_unlock(&irq_controller_lock);*/
+ /*if ( irq_controller_lock.lock )
+ panic("irq controller lock still held in mask and ack\n");*/
}
void chrp_mask_and_ack_irq(int irq_nr)
@@ -188,44 +256,96 @@
else
openpic_enable_irq(irq_to_openpic(irq_nr));
}
+#else /* CONFIG_8xx */
+static void mbx_mask_irq(unsigned int irq_nr)
+{
+ cached_irq_mask &= ~(1 << (31-irq_nr));
+ ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask =
+ cached_irq_mask;
+}
+
+static void mbx_unmask_irq(unsigned int irq_nr)
+{
+ cached_irq_mask |= (1 << (31-irq_nr));
+ ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask =
+ cached_irq_mask;
+}
+#endif /* CONFIG_8xx */
void disable_irq(unsigned int irq_nr)
{
- unsigned long flags;
+ /*unsigned long flags;*/
- spin_lock_irqsave(&irq_controller_lock, flags);
+ /* spin_lock_irqsave(&irq_controller_lock, flags);*/
mask_irq(irq_nr);
- spin_unlock_irqrestore(&irq_controller_lock, flags);
+ /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/
synchronize_irq();
}
void enable_irq(unsigned int irq_nr)
{
- unsigned long flags;
+ /*unsigned long flags;*/
- spin_lock_irqsave(&irq_controller_lock, flags);
+ /* spin_lock_irqsave(&irq_controller_lock, flags);*/
unmask_irq(irq_nr);
- spin_unlock_irqrestore(&irq_controller_lock, flags);
+ /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/
}
int get_irq_list(char *buf)
{
- int i, len = 0;
+ int i, len = 0, j;
struct irqaction * action;
+ len += sprintf(buf+len, " ");
+ for (j=0; j<smp_num_cpus; j++)
+ len += sprintf(buf+len, "CPU%d ",j);
+ *(char *)(buf+len++) = '\n';
+
for (i = 0 ; i < NR_IRQS ; i++) {
- action = irq_action + i;
+ action = irq_action[i];
if (!action || !action->handler)
continue;
- len += sprintf(buf+len, "%2d: %10u %s",
- i, kstat.interrupts[i], action->name);
+ len += sprintf(buf+len, "%3d: ", i);
+#ifdef __SMP__
+ for (j = 0; j < smp_num_cpus; j++)
+ len += sprintf(buf+len, "%10u ",
+ kstat.irqs[cpu_logical_map(j)][i]);
+#else
+ len += sprintf(buf+len, "%10u ", kstat_irqs(i));
+#endif /* __SMP__ */
+ switch( _machine )
+ {
+ case _MACH_prep:
+ len += sprintf(buf+len, " 82c59 ");
+ break;
+ case _MACH_Pmac:
+ len += sprintf(buf+len, " PMAC-PIC ");
+ break;
+ case _MACH_chrp:
+ if ( is_8259_irq(i) )
+ len += sprintf(buf+len, " 82c59 ");
+ else
+ len += sprintf(buf+len, " OpenPIC ");
+ break;
+ }
+
+ len += sprintf(buf+len, " %s",action->name);
for (action=action->next; action; action = action->next) {
len += sprintf(buf+len, ", %s", action->name);
}
len += sprintf(buf+len, "\n");
}
- len += sprintf(buf+len, "99: %10u spurious or short\n",
- spurious_interrupts);
+#ifdef __SMP__
+ /* should this be per processor send/receive? */
+ len += sprintf(buf+len, "IPI: %10lu\n", ipi_count);
+ for ( i = 0 ; i <= smp_num_cpus-1; i++ )
+ len += sprintf(buf+len," ");
+ len += sprintf(buf+len, " interprocessor messages received\n");
+#endif
+ len += sprintf(buf+len, "BAD: %10u",spurious_interrupts);
+ for ( i = 0 ; i <= smp_num_cpus-1; i++ )
+ len += sprintf(buf+len," ");
+ len += sprintf(buf+len, " spurious or short\n");
return len;
}
@@ -394,20 +514,31 @@
int status;
int openpic_eoi_done = 0;
+ /* save the HID0 in case dcache was off - see idle.c
+ * this hack should leave for a better solution -- Cort */
+ unsigned dcache_locked;
+
+ dcache_locked = unlock_dcache();
+ hardirq_enter(cpu);
+#ifndef CONFIG_8xx
#ifdef __SMP__
if ( cpu != 0 )
- panic("cpu %d received interrupt", cpu);
-#endif /* __SMP__ */
-
- hardirq_enter(cpu);
+ {
+ if ( !lost_interrupts )
+ {
+ extern smp_message_recv(void);
+ goto out;
+
+ ipi_count++;
+ smp_message_recv();
+ goto out;
+ }
+ /* could be here due to a do_fake_interrupt call but we don't
+ mess with the controller from the second cpu -- Cort */
+ goto out;
+ }
+#endif /* __SMP__ */
- /*
- * I'll put this ugly mess of code into a function
- * such as get_pending_irq() or some such clear thing
- * so we don't have a switch in the irq code and
- * the chrp code is merged a bit with the prep.
- * -- Cort
- */
switch ( _machine )
{
case _MACH_Pmac:
@@ -425,7 +556,7 @@
*
* This should go in the above mask/ack code soon. -- Cort
*/
- irq = *(volatile unsigned char *)GG2_INT_ACK_SPECIAL;
+ irq = *gg2_int_ack_special;
/*
* Acknowledge as soon as possible to allow i8259
* interrupt nesting
@@ -456,7 +587,6 @@
}
break;
case _MACH_prep:
-#if 1
outb(0x0C, 0x20);
irq = inb(0x20) & 7;
if (irq == 2)
@@ -470,23 +600,6 @@
irq = (irq&7) + 8;
}
bits = 1UL << irq;
-#else
- /*
- * get the isr from the intr controller since
- * the bit in the irr has been cleared
- */
- outb(0x0a, 0x20);
- bits = inb(0x20)&0xff;
- /* handle cascade */
- if ( bits & 4 )
- {
- bits &= ~4UL;
- outb(0x0a, 0xA0);
- bits |= inb(0xA0)<<8;
- }
- /* ignore masked irqs */
- bits &= ~cached_irq_mask;
-#endif
break;
}
@@ -494,43 +607,62 @@
printk("Bogus interrupt from PC = %lx\n", regs->nip);
goto out;
}
+
+#else /* CONFIG_8xx */
+ /* For MPC8xx, read the SIVEC register and shift the bits down
+ * to get the irq number.
+ */
+ bits = ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_sivec;
+ irq = bits >> 26;
+#endif /* CONFIG_8xx */
mask_and_ack_irq(irq);
status = 0;
- action = irq_action + irq;
- kstat.interrupts[irq]++;
- if (action->handler) {
+ action = irq_action[irq];
+ kstat.irqs[cpu][irq]++;
+ if ( action && action->handler) {
if (!(action->flags & SA_INTERRUPT))
__sti();
- status |= action->flags;
- action->handler(irq, action->dev_id, regs);
- /*if (status & SA_SAMPLE_RANDOM)
- add_interrupt_randomness(irq);*/
- __cli(); /* in case the handler turned them on */
- spin_lock(&irq_controller_lock);
+ do {
+ status |= action->flags;
+ action->handler(irq, action->dev_id, regs);
+ /*if (status & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);*/
+ action = action->next;
+ } while ( action );
+ __cli();
+ /* spin_lock(&irq_controller_lock);*/
unmask_irq(irq);
- spin_unlock(&irq_controller_lock);
+ /* spin_unlock(&irq_controller_lock);*/
} else {
+#ifndef CONFIG_8xx
if ( irq == 7 ) /* i8259 gives us irq 7 on 'short' intrs */
+#endif
spurious_interrupts++;
disable_irq( irq );
}
/* make sure we don't miss any cascade intrs due to eoi-ing irq 2 */
+#ifndef CONFIG_8xx
if ( is_prep && (irq > 7) )
goto retry_cascade;
/* do_bottom_half is called if necessary from int_return in head.S */
out:
if (_machine == _MACH_chrp && !openpic_eoi_done)
openpic_eoi(0);
+#endif /* CONFIG_8xx */
hardirq_exit(cpu);
+
+ /* restore the HID0 in case dcache was off - see idle.c
+ * this hack should leave for a better solution -- Cort */
+ lock_dcache(dcache_locked);
}
int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags, const char * devname, void *dev_id)
{
- struct irqaction * action;
+ struct irqaction *old, **p, *action;
unsigned long flags;
#ifdef SHOW_IRQ
@@ -540,49 +672,58 @@
if (irq >= NR_IRQS)
return -EINVAL;
- action = irq + irq_action;
- if (action->handler)
- return -EBUSY;
+
if (!handler)
- return -EINVAL;
+ {
+ /* Free */
+ for (p = irq + irq_action; (action = *p) != NULL; p = &action->next)
+ {
+ /* Found it - now free it */
+ save_flags(flags);
+ cli();
+ *p = action->next;
+ restore_flags(flags);
+ irq_kfree(action);
+ return 0;
+ }
+ return -ENOENT;
+ }
+
+ action = (struct irqaction *)
+ irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+ if (!action)
+ return -ENOMEM;
save_flags(flags);
cli();
+
action->handler = handler;
action->flags = irqflags;
action->mask = 0;
action->name = devname;
action->dev_id = dev_id;
+ action->next = NULL;
enable_irq(irq);
- restore_flags(flags);
+ p = irq_action + irq;
+
+ if ((old = *p) != NULL) {
+ /* Can't share interrupts unless both agree to */
+ if (!(old->flags & action->flags & SA_SHIRQ))
+ return -EBUSY;
+ /* add new interrupt at end of irq queue */
+ do {
+ p = &old->next;
+ old = *p;
+ } while (old);
+ }
+ *p = action;
+
+ restore_flags(flags);
return 0;
}
void free_irq(unsigned int irq, void *dev_id)
{
- struct irqaction * action = irq + irq_action;
- unsigned long flags;
-
-#ifdef SHOW_IRQ
- printk("free_irq(): irq %d dev_id %04x\n", irq, dev_id);
-#endif /* SHOW_IRQ */
-
- if (irq >= NR_IRQS) {
- printk("Trying to free IRQ%d\n",irq);
- return;
- }
- if (!action->handler) {
- printk("Trying to free free IRQ%d\n",irq);
- return;
- }
- disable_irq(irq);
- save_flags(flags);
- cli();
- action->handler = NULL;
- action->flags = 0;
- action->mask = 0;
- action->name = NULL;
- action->dev_id = NULL;
- restore_flags(flags);
+ request_irq(irq, NULL, 0, NULL, dev_id);
}
unsigned long probe_irq_on (void)
@@ -595,6 +736,7 @@
return 0;
}
+#ifndef CONFIG_8xx
__initfunc(static void i8259_init(void))
{
/* init master interrupt controller */
@@ -616,11 +758,17 @@
panic("Could not allocate cascade IRQ!");
enable_irq(2); /* Enable cascade interrupt */
}
+#endif /* CONFIG_8xx */
+/* On MBX8xx, the interrupt control (SIEL) was set by EPPC-bug. External
+ * interrupts can be either edge or level triggered, but there is no
+ * reason for us to change the EPPC-bug values (it would not work if we did).
+ */
__initfunc(void init_IRQ(void))
{
extern void xmon_irq(int, void *, struct pt_regs *);
+#ifndef CONFIG_8xx
switch (_machine)
{
case _MACH_Pmac:
@@ -637,7 +785,8 @@
mask_and_ack_irq = chrp_mask_and_ack_irq;
mask_irq = chrp_mask_irq;
unmask_irq = chrp_unmask_irq;
- ioremap(GG2_INT_ACK_SPECIAL, 1);
+ gg2_int_ack_special = (volatile unsigned char *)
+ ioremap(GG2_INT_ACK_SPECIAL, 1);
openpic_init();
i8259_init();
#ifdef CONFIG_XMON
@@ -653,7 +802,7 @@
i8259_init();
route_pci_interrupts();
/*
- * According to the Carolina spec from ibm irq's 0,1,2, and 8
+ * According to the Carolina spec from ibm irqs 0,1,2, and 8
* must be edge triggered. Also, the pci intrs must be level
* triggered and _only_ isa intrs can be level sensitive
* which are 3-7,9-12,14-15. 13 is special - it can be level.
@@ -686,5 +835,6 @@
}
break;
- }
+ }
+#endif /* CONFIG_8xx */
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov