diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-08-25 09:12:35 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-08-25 09:12:35 +0000 |
commit | c7fc24dc4420057f103afe8fc64524ebc25c5d37 (patch) | |
tree | 3682407a599b8f9f03fc096298134cafba1c9b2f /arch/ppc/kernel/irq.c | |
parent | 1d793fade8b063fde3cf275bf1a5c2d381292cd9 (diff) |
o Merge with Linux 2.1.116.
o New Newport console code.
o New G364 console code.
Diffstat (limited to 'arch/ppc/kernel/irq.c')
-rw-r--r-- | arch/ppc/kernel/irq.c | 203 |
1 files changed, 150 insertions, 53 deletions
diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index 462805a65..cb4d0872e 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -8,6 +8,7 @@ * Updated and modified by Cort Dougan (cort@cs.nmt.edu) * Adapted for Power Macintosh by Paul Mackerras * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * This file contains the code used by various IRQ handling routines: * asking for different IRQ's should be done through these routines @@ -38,8 +39,8 @@ #include <linux/malloc.h> #include <linux/openpic.h> #include <linux/pci.h> -#include <linux/openpic.h> +#include <asm/bitops.h> #include <asm/hydra.h> #include <asm/system.h> #include <asm/io.h> @@ -48,23 +49,41 @@ #include <asm/bitops.h> #include <asm/gg2.h> #include <asm/cache.h> +#include <asm/prom.h> #ifdef CONFIG_8xx #include <asm/8xx_immap.h> #include <asm/mbx.h> #endif +#include <asm/amigaints.h> +#include <asm/amigahw.h> +#include <asm/amigappc.h> +#define VEC_SPUR (24) +extern void process_int(unsigned long vec, struct pt_regs *fp); +extern void apus_init_IRQ(void); +extern void amiga_disable_irq(unsigned int irq); +extern void amiga_enable_irq(unsigned int irq); +#ifdef CONFIG_APUS +/* Rename a few functions. Requires the CONFIG_APUS protection. */ +#define request_irq nop_ppc_request_irq +#define free_irq nop_ppc_free_irq +#define get_irq_list nop_get_irq_list +#endif + #undef SHOW_IRQ -unsigned lost_interrupts = 0; +#define NR_MASK_WORDS ((NR_IRQS + 31) / 32) + +int max_irqs; unsigned int local_irq_count[NR_CPUS]; 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 unsigned int cached_irq_mask[NR_MASK_WORDS]; +unsigned int lost_interrupts[NR_MASK_WORDS]; +atomic_t n_lost_interrupts; + static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } + /*spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED;*/ #ifdef __SMP__ atomic_t __ppc_bh_counter = ATOMIC_INIT(0); @@ -74,8 +93,8 @@ int __ppc_bh_counter = 0; 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]) +#define cached_21 (((char *)(cached_irq_mask))[3]) +#define cached_A1 (((char *)(cached_irq_mask))[2]) /* * These are set to the appropriate functions by init_IRQ() @@ -97,16 +116,22 @@ void (*unmask_irq)(unsigned int irq_nr); /* prep */ #define PREP_IRQ_MASK (((unsigned int)cached_A1)<<8) | (unsigned int)cached_21 -extern unsigned long route_pci_interrupts(void); /* pmac */ -#define IRQ_FLAG ((unsigned *)0xf3000020) -#define IRQ_ENABLE ((unsigned *)0xf3000024) -#define IRQ_ACK ((unsigned *)0xf3000028) -#define IRQ_LEVEL ((unsigned *)0xf300002c) -#define KEYBOARD_IRQ 20 /* irq number for command-power interrupt */ -#define PMAC_IRQ_MASK (~ld_le32(IRQ_ENABLE)) +struct pmac_irq_hw { + unsigned int flag; + unsigned int enable; + unsigned int ack; + unsigned int level; +}; + +/* XXX these addresses should be obtained from the device tree */ +volatile struct pmac_irq_hw *pmac_irq_hw[2] = { + (struct pmac_irq_hw *) 0xf3000020, + (struct pmac_irq_hw *) 0xf3000010, +}; +#define KEYBOARD_IRQ 20 /* irq number for command-power interrupt */ /* nasty hack for shared irq's since we need to do kmalloc calls but @@ -148,7 +173,7 @@ void irq_kfree(void *ptr) void i8259_mask_and_ack_irq(int irq_nr) { /* spin_lock(&irq_controller_lock);*/ - cached_irq_mask |= 1 << irq_nr; + cached_irq_mask[0] |= 1 << irq_nr; if (irq_nr > 7) { inb(0xA1); /* DUMMY */ outb(cached_A1,0xA1); @@ -167,17 +192,23 @@ void i8259_mask_and_ack_irq(int irq_nr) void pmac_mask_and_ack_irq(int irq_nr) { - unsigned long bit = 1UL << irq_nr; + unsigned long bit = 1UL << (irq_nr & 0x1f); + int i = irq_nr >> 5; - /* 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);*/ + if (irq_nr >= max_irqs) + return; + /*spin_lock(&irq_controller_lock);*/ + + clear_bit(irq_nr, cached_irq_mask); + if (test_and_clear_bit(irq_nr, lost_interrupts)) + atomic_dec(&n_lost_interrupts); + out_le32(&pmac_irq_hw[i]->ack, bit); + out_le32(&pmac_irq_hw[i]->enable, cached_irq_mask[i]); + out_le32(&pmac_irq_hw[i]->ack, bit); + + /*spin_unlock(&irq_controller_lock);*/ /*if ( irq_controller_lock.lock ) - panic("irq controller lock still held in mask and ack\n");*/ + panic("irq controller lock still held in mask and ack\n");*/ } void chrp_mask_and_ack_irq(int irq_nr) @@ -199,18 +230,26 @@ static void i8259_set_irq_mask(int irq_nr) static void pmac_set_irq_mask(int irq_nr) { - unsigned long bit = 1UL << irq_nr; + unsigned long bit = 1UL << (irq_nr & 0x1f); + int i = irq_nr >> 5; + + if (irq_nr >= max_irqs) + return; + + /* enable unmasked interrupts */ + out_le32(&pmac_irq_hw[i]->enable, cached_irq_mask[i]); - /* this could be being enabled or disabled - so use cached_irq_mask */ - out_le32(IRQ_ENABLE, ~cached_irq_mask /* enable all unmasked */ ); /* * Unfortunately, setting the bit in the enable register * when the device interrupt is already on *doesn't* set * the bit in the flag register or request another interrupt. */ - if ((bit & ~cached_irq_mask) - && (ld_le32(IRQ_LEVEL) & bit) && !(ld_le32(IRQ_FLAG) & bit)) - lost_interrupts |= bit; + if ((bit & cached_irq_mask[i]) + && (ld_le32(&pmac_irq_hw[i]->level) & bit) + && !(ld_le32(&pmac_irq_hw[i]->flag) & bit)) { + if (!test_and_set_bit(irq_nr, lost_interrupts)) + atomic_inc(&n_lost_interrupts); + } } /* @@ -219,25 +258,25 @@ static void pmac_set_irq_mask(int irq_nr) */ static void i8259_mask_irq(unsigned int irq_nr) { - cached_irq_mask |= 1 << irq_nr; + cached_irq_mask[0] |= 1 << irq_nr; i8259_set_irq_mask(irq_nr); } static void i8259_unmask_irq(unsigned int irq_nr) { - cached_irq_mask &= ~(1 << irq_nr); + cached_irq_mask[0] &= ~(1 << irq_nr); i8259_set_irq_mask(irq_nr); } static void pmac_mask_irq(unsigned int irq_nr) { - cached_irq_mask |= 1 << irq_nr; + clear_bit(irq_nr, cached_irq_mask); pmac_set_irq_mask(irq_nr); } static void pmac_unmask_irq(unsigned int irq_nr) { - cached_irq_mask &= ~(1 << irq_nr); + set_bit(irq_nr, cached_irq_mask); pmac_set_irq_mask(irq_nr); } @@ -259,16 +298,16 @@ static void chrp_unmask_irq(unsigned int irq_nr) #else /* CONFIG_8xx */ static void mbx_mask_irq(unsigned int irq_nr) { - cached_irq_mask &= ~(1 << (31-irq_nr)); + cached_irq_mask[0] &= ~(1 << (31-irq_nr)); ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask = - cached_irq_mask; + cached_irq_mask[0]; } static void mbx_unmask_irq(unsigned int irq_nr) { - cached_irq_mask |= (1 << (31-irq_nr)); + cached_irq_mask[0] |= (1 << (31-irq_nr)); ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask = - cached_irq_mask; + cached_irq_mask[0]; } #endif /* CONFIG_8xx */ @@ -327,6 +366,9 @@ int get_irq_list(char *buf) else len += sprintf(buf+len, " OpenPIC "); break; + case _MACH_mbx: + len += sprintf(buf+len, " MPC8xx "); + break; } len += sprintf(buf+len, " %s",action->name); @@ -524,9 +566,9 @@ asmlinkage void do_IRQ(struct pt_regs *regs) #ifdef __SMP__ if ( cpu != 0 ) { - if ( !lost_interrupts ) + if (!atomic_read(&n_lost_interrupts)) { - extern smp_message_recv(void); + extern void smp_message_recv(void); goto out; ipi_count++; @@ -542,10 +584,17 @@ asmlinkage void do_IRQ(struct pt_regs *regs) switch ( _machine ) { case _MACH_Pmac: - bits = ld_le32(IRQ_FLAG) | lost_interrupts; - for (irq = NR_IRQS - 1; irq >= 0; --irq) - if (bits & (1U << irq)) - break; + for (irq = max_irqs - 1; irq > 0; irq -= 32) { + int i = irq >> 5, lz; + bits = ld_le32(&pmac_irq_hw[i]->flag) + | lost_interrupts[i]; + if (bits == 0) + continue; + /* lz = number of 0 bits to left of most sig. 1 */ + asm ("cntlzw %0,%1" : "=r" (lz) : "r" (bits)); + irq -= lz; + break; + } break; case _MACH_chrp: irq = openpic_irq(0); @@ -601,10 +650,41 @@ retry_cascade: } bits = 1UL << irq; break; +#ifdef CONFIG_APUS + case _MACH_apus: + { + int old_level, new_level; + + old_level = ~(regs->mq) & IPLEMU_IPLMASK; + new_level = (~(regs->mq) >> 3) & IPLEMU_IPLMASK; + + if (new_level == 0) + { + goto apus_out; + } + + APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK); + APUS_WRITE(APUS_IPL_EMU, (IPLEMU_SETRESET + | (~(new_level) & IPLEMU_IPLMASK))); + APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); + + process_int (VEC_SPUR+new_level, regs); + + APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | IPLEMU_DISABLEINT); + APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK); + APUS_WRITE(APUS_IPL_EMU, (IPLEMU_SETRESET + | (~(old_level) & IPLEMU_IPLMASK))); + +apus_out: + hardirq_exit(cpu); + APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); + goto out2; + } +#endif } if (irq < 0) { - printk("Bogus interrupt from PC = %lx\n", regs->nip); + printk(KERN_DEBUG "Bogus interrupt from PC = %lx\n", regs->nip); goto out; } @@ -621,7 +701,7 @@ retry_cascade: status = 0; action = irq_action[irq]; kstat.irqs[cpu][irq]++; - if ( action && action->handler) { + if (action && action->handler) { if (!(action->flags & SA_INTERRUPT)) __sti(); do { @@ -653,7 +733,10 @@ out: openpic_eoi(0); #endif /* CONFIG_8xx */ hardirq_exit(cpu); - + +#ifdef CONFIG_APUS +out2: +#endif /* restore the HID0 in case dcache was off - see idle.c * this hack should leave for a better solution -- Cort */ lock_dcache(dcache_locked); @@ -767,6 +850,7 @@ __initfunc(static void i8259_init(void)) __initfunc(void init_IRQ(void)) { extern void xmon_irq(int, void *, struct pt_regs *); + int i; #ifndef CONFIG_8xx switch (_machine) @@ -775,8 +859,13 @@ __initfunc(void init_IRQ(void)) mask_and_ack_irq = pmac_mask_and_ack_irq; mask_irq = pmac_mask_irq; unmask_irq = pmac_unmask_irq; - - *IRQ_ENABLE = 0; + + /* G3 powermacs have 64 interrupts, others have 32 */ + max_irqs = (find_devices("mac-io") ? 64 : 32); + printk("System has %d possible interrupts\n", max_irqs); + + for (i = 0; i * 32 < max_irqs; ++i) + out_le32(&pmac_irq_hw[i]->enable, 0); #ifdef CONFIG_XMON request_irq(KEYBOARD_IRQ, xmon_irq, 0, "NMI", 0); #endif /* CONFIG_XMON */ @@ -789,6 +878,7 @@ __initfunc(void init_IRQ(void)) ioremap(GG2_INT_ACK_SPECIAL, 1); openpic_init(); i8259_init(); + cached_irq_mask[0] = cached_irq_mask[1] = ~0UL; #ifdef CONFIG_XMON request_irq(openpic_to_irq(HYDRA_INT_ADB_NMI), xmon_irq, 0, "NMI", 0); @@ -798,9 +888,9 @@ __initfunc(void init_IRQ(void)) mask_and_ack_irq = i8259_mask_and_ack_irq; mask_irq = i8259_mask_irq; unmask_irq = i8259_unmask_irq; + cached_irq_mask[0] = ~0UL; i8259_init(); - route_pci_interrupts(); /* * According to the Carolina spec from ibm irqs 0,1,2, and 8 * must be edge triggered. Also, the pci intrs must be level @@ -835,6 +925,13 @@ __initfunc(void init_IRQ(void)) } break; - } +#ifdef CONFIG_APUS + case _MACH_apus: + mask_irq = amiga_disable_irq; + unmask_irq = amiga_enable_irq; + apus_init_IRQ(); + break; +#endif + } #endif /* CONFIG_8xx */ } |