diff options
Diffstat (limited to 'arch/ppc/amiga')
-rw-r--r-- | arch/ppc/amiga/Makefile | 3 | ||||
-rw-r--r-- | arch/ppc/amiga/amiints.c | 50 | ||||
-rw-r--r-- | arch/ppc/amiga/cia.c | 236 | ||||
-rw-r--r-- | arch/ppc/amiga/config.c | 5 |
4 files changed, 259 insertions, 35 deletions
diff --git a/arch/ppc/amiga/Makefile b/arch/ppc/amiga/Makefile index 2d5ec4811..8288eabc2 100644 --- a/arch/ppc/amiga/Makefile +++ b/arch/ppc/amiga/Makefile @@ -9,7 +9,8 @@ O_TARGET := amiga.o O_OBJS := config.o amiints.o cia.o time.o \ - bootinfo.o amisound.o chipram.o ints.o + bootinfo.o amisound.o chipram.o + OX_OBJS := amiga_ksyms.o ifdef CONFIG_AMIGA_PCMCIA diff --git a/arch/ppc/amiga/amiints.c b/arch/ppc/amiga/amiints.c index 408f8211c..75d90312b 100644 --- a/arch/ppc/amiga/amiints.c +++ b/arch/ppc/amiga/amiints.c @@ -53,10 +53,10 @@ #include <asm/amigappc.h> #endif -extern int cia_request_irq(struct ciabase *base,int irq, +extern int cia_request_irq(int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id); -extern void cia_free_irq(struct ciabase *base, unsigned int irq, void *dev_id); +extern void cia_free_irq(unsigned int irq, void *dev_id); extern void cia_init_IRQ(struct ciabase *base); extern int cia_get_irq_list(struct ciabase *base, char *buf); @@ -89,7 +89,8 @@ static void ami_badint(int irq, void *dev_id, struct pt_regs *fp) * the amiga IRQ handling routines. */ -void __init amiga_init_IRQ(void) +__init +void amiga_init_IRQ(void) { int i; @@ -216,13 +217,8 @@ int amiga_request_irq(unsigned int irq, return sys_request_irq(irq - IRQ_AMIGA_AUTO, handler, flags, devname, dev_id); - if (irq >= IRQ_AMIGA_CIAB) - return cia_request_irq(&ciab_base, irq - IRQ_AMIGA_CIAB, - handler, flags, devname, dev_id); - if (irq >= IRQ_AMIGA_CIAA) - return cia_request_irq(&ciaa_base, irq - IRQ_AMIGA_CIAA, - handler, flags, devname, dev_id); + return cia_request_irq(irq, handler, flags, devname, dev_id); /* * IRQ_AMIGA_PORTS & IRQ_AMIGA_EXTER defaults to shared, @@ -262,13 +258,8 @@ void amiga_free_irq(unsigned int irq, void *dev_id) if (irq >= IRQ_AMIGA_AUTO) sys_free_irq(irq - IRQ_AMIGA_AUTO, dev_id); - if (irq >= IRQ_AMIGA_CIAB) { - cia_free_irq(&ciab_base, irq - IRQ_AMIGA_CIAB, dev_id); - return; - } - if (irq >= IRQ_AMIGA_CIAA) { - cia_free_irq(&ciaa_base, irq - IRQ_AMIGA_CIAA, dev_id); + cia_free_irq(irq, dev_id); return; } @@ -314,17 +305,9 @@ void amiga_enable_irq(unsigned int irq) return; } - if (irq >= IRQ_AMIGA_CIAB) { - cia_set_irq(&ciab_base, (1 << (irq - IRQ_AMIGA_CIAB))); - cia_able_irq(&ciab_base, CIA_ICR_SETCLR | - (1 << (irq - IRQ_AMIGA_CIAB))); - return; - } - if (irq >= IRQ_AMIGA_CIAA) { - cia_set_irq(&ciaa_base, (1 << (irq - IRQ_AMIGA_CIAA))); - cia_able_irq(&ciaa_base, CIA_ICR_SETCLR | - (1 << (irq - IRQ_AMIGA_CIAA))); + cia_set_irq(irq, 0); + cia_able_irq(irq, 1); return; } @@ -349,13 +332,8 @@ void amiga_disable_irq(unsigned int irq) return; } - if (irq >= IRQ_AMIGA_CIAB) { - cia_able_irq(&ciab_base, 1 << (irq - IRQ_AMIGA_CIAB)); - return; - } - if (irq >= IRQ_AMIGA_CIAA) { - cia_able_irq(&ciaa_base, 1 << (irq - IRQ_AMIGA_CIAA)); + cia_able_irq(irq, 0); return; } @@ -528,10 +506,20 @@ static void ami_int7(int irq, void *dev_id, struct pt_regs *fp) panic ("level 7 interrupt received\n"); } +#ifdef CONFIG_APUS +/* The PPC irq handling links all handlers requested on the same vector + and executes them in a loop. Having ami_badint at the end of the chain + is a bad idea. */ +void (*amiga_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = { + NULL, ami_int1, NULL, NULL /* FB expects to replace ami_int3*/, + ami_int4, ami_int5, NULL, ami_int7 +}; +#else void (*amiga_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = { ami_badint, ami_int1, ami_badint, ami_int3, ami_int4, ami_int5, ami_badint, ami_int7 }; +#endif int amiga_get_irq_list(char *buf) { diff --git a/arch/ppc/amiga/cia.c b/arch/ppc/amiga/cia.c index ea35c79b3..85133f8da 100644 --- a/arch/ppc/amiga/cia.c +++ b/arch/ppc/amiga/cia.c @@ -1 +1,235 @@ -#include "../../m68k/amiga/cia.c" +/* + * linux/arch/m68k/amiga/cia.c - CIA support + * + * Copyright (C) 1996 Roman Zippel + * + * The concept of some functions bases on the original Amiga OS function + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/init.h> + +#include <asm/irq.h> +#include <asm/amigahw.h> +#include <asm/amigaints.h> + +struct ciabase { + volatile struct CIA *cia; + u_char icr_mask, icr_data; + u_short int_mask; + int handler_irq, cia_irq, server_irq; + char *name; + struct irq_server server; + irq_handler_t irq_list[CIA_IRQS]; +} ciaa_base = { + &ciaa, 0, 0, IF_PORTS, + IRQ_AMIGA_AUTO_2, IRQ_AMIGA_CIAA, + IRQ_AMIGA_PORTS, + "CIAA handler", {0, 0} +}, ciab_base = { + &ciab, 0, 0, IF_EXTER, + IRQ_AMIGA_AUTO_6, IRQ_AMIGA_CIAB, + IRQ_AMIGA_EXTER, + "CIAB handler", {0, 0} +}; + +#define CIA_SET_BASE_ADJUST_IRQ(base, irq) \ +do { \ + if (irq >= IRQ_AMIGA_CIAB) { \ + base = &ciab_base; \ + irq =- IRQ_AMIGA_CIAB; \ + } else { \ + base = &ciaa_base; \ + irq =- IRQ_AMIGA_CIAA; \ + } \ +} while (0) + +/* + * Cause or clear CIA interrupts, return old interrupt status. + */ + +static unsigned char cia_set_irq_private(struct ciabase *base, + unsigned char mask) +{ + u_char old; + + old = (base->icr_data |= base->cia->icr); + if (mask & CIA_ICR_SETCLR) + base->icr_data |= mask; + else + base->icr_data &= ~mask; + if (base->icr_data & base->icr_mask) + custom.intreq = IF_SETCLR | base->int_mask; + return old & base->icr_mask; +} + +unsigned char cia_set_irq(unsigned int irq, int set) +{ + struct ciabase *base; + unsigned char mask; + + if (irq >= IRQ_AMIGA_CIAB) + mask = (1 << (irq - IRQ_AMIGA_CIAB)); + else + mask = (1 << (irq - IRQ_AMIGA_CIAA)); + mask |= (set) ? CIA_ICR_SETCLR : 0; + + CIA_SET_BASE_ADJUST_IRQ(base, irq); + + return cia_set_irq_private(base, mask); +} + +unsigned char cia_get_irq_mask(unsigned int irq) +{ + struct ciabase *base; + + CIA_SET_BASE_ADJUST_IRQ(base, irq); + + return base->cia->icr; +} + +/* + * Enable or disable CIA interrupts, return old interrupt mask, + * interrupts will only be enabled if a handler exists + */ + +static unsigned char cia_able_irq_private(struct ciabase *base, + unsigned char mask) +{ + u_char old, tmp; + int i; + + old = base->icr_mask; + base->icr_data |= base->cia->icr; + base->cia->icr = mask; + if (mask & CIA_ICR_SETCLR) + base->icr_mask |= mask; + else + base->icr_mask &= ~mask; + base->icr_mask &= CIA_ICR_ALL; + for (i = 0, tmp = 1; i < CIA_IRQS; i++, tmp <<= 1) { + if ((tmp & base->icr_mask) && !base->irq_list[i].handler) { + base->icr_mask &= ~tmp; + base->cia->icr = tmp; + } + } + if (base->icr_data & base->icr_mask) + custom.intreq = IF_SETCLR | base->int_mask; + return old; +} + +unsigned char cia_able_irq(unsigned int irq, int enable) +{ + struct ciabase *base; + unsigned char mask; + + if (irq >= IRQ_AMIGA_CIAB) + mask = (1 << (irq - IRQ_AMIGA_CIAB)); + else + mask = (1 << (irq - IRQ_AMIGA_CIAA)); + mask |= (enable) ? CIA_ICR_SETCLR : 0; + + CIA_SET_BASE_ADJUST_IRQ(base, irq); + + return cia_able_irq_private(base, mask); +} + +int cia_request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long flags, const char *devname, void *dev_id) +{ + u_char mask; + struct ciabase *base; + + CIA_SET_BASE_ADJUST_IRQ(base, irq); + + base->irq_list[irq].handler = handler; + base->irq_list[irq].flags = flags; + base->irq_list[irq].dev_id = dev_id; + base->irq_list[irq].devname = devname; + + /* enable the interrupt */ + mask = 1 << irq; + cia_set_irq_private(base, mask); + cia_able_irq_private(base, CIA_ICR_SETCLR | mask); + return 0; +} + +void cia_free_irq(unsigned int irq, void *dev_id) +{ + struct ciabase *base; + + CIA_SET_BASE_ADJUST_IRQ(base, irq); + + if (base->irq_list[irq].dev_id != dev_id) + printk("%s: removing probably wrong IRQ %i from %s\n", + __FUNCTION__, base->cia_irq + irq, + base->irq_list[irq].devname); + + base->irq_list[irq].handler = NULL; + base->irq_list[irq].flags = 0; + + cia_able_irq_private(base, 1 << irq); +} + +static void cia_handler(int irq, void *dev_id, struct pt_regs *fp) +{ + struct ciabase *base = (struct ciabase *)dev_id; + int mach_irq, i; + unsigned char ints; + + mach_irq = base->cia_irq; + irq = SYS_IRQS + mach_irq; + ints = cia_set_irq_private(base, CIA_ICR_ALL); + custom.intreq = base->int_mask; + for (i = 0; i < CIA_IRQS; i++, irq++, mach_irq++) { + if (ints & 1) { + kstat.irqs[0][irq]++; + base->irq_list[i].handler(mach_irq, base->irq_list[i].dev_id, fp); + } + ints >>= 1; + } + amiga_do_irq_list(base->server_irq, fp, &base->server); +} + +void __init cia_init_IRQ(struct ciabase *base) +{ + int i; + + /* init isr handlers */ + for (i = 0; i < CIA_IRQS; i++) { + base->irq_list[i].handler = NULL; + base->irq_list[i].flags = 0; + } + + /* clear any pending interrupt and turn off all interrupts */ + cia_set_irq_private(base, CIA_ICR_ALL); + cia_able_irq_private(base, CIA_ICR_ALL); + + /* install CIA handler */ + request_irq(base->handler_irq, cia_handler, 0, base->name, base); + + custom.intena = IF_SETCLR | base->int_mask; +} + +int cia_get_irq_list(struct ciabase *base, char *buf) +{ + int i, j, len = 0; + + j = base->cia_irq; + for (i = 0; i < CIA_IRQS; i++) { + len += sprintf(buf+len, "cia %2d: %10d ", j + i, + kstat.irqs[0][SYS_IRQS + j + i]); + len += sprintf(buf+len, " "); + len += sprintf(buf+len, "%s\n", base->irq_list[i].devname); + } + return len; +} diff --git a/arch/ppc/amiga/config.c b/arch/ppc/amiga/config.c index 47b8f64ea..652eb1582 100644 --- a/arch/ppc/amiga/config.c +++ b/arch/ppc/amiga/config.c @@ -6,7 +6,6 @@ void (*mach_kbd_reset_setup) (char *, int) __initdata = 0; #include <asm/io.h> -#include <asm/system.h> /* * linux/arch/m68k/amiga/config.c @@ -461,6 +460,8 @@ static void __init amiga_sched_init(void (*timer_routine)(int, void *, #define TICK_SIZE 10000 +extern unsigned char cia_get_irq_mask(unsigned int irq); + /* This is always executed with interrupts disabled. */ static unsigned long amiga_gettimeoffset (void) { @@ -481,7 +482,7 @@ static unsigned long amiga_gettimeoffset (void) if (ticks > jiffy_ticks / 2) /* check for pending interrupt */ - if (cia_set_irq(&ciab_base, 0) & CIA_ICR_TA) + if (cia_get_irq_mask(IRQ_AMIGA_CIAB) & CIA_ICR_TA) offset = 10000; ticks = jiffy_ticks - ticks; |