summaryrefslogtreecommitdiffstats
path: root/arch/ppc/amiga
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ppc/amiga')
-rw-r--r--arch/ppc/amiga/Makefile3
-rw-r--r--arch/ppc/amiga/amiints.c50
-rw-r--r--arch/ppc/amiga/cia.c236
-rw-r--r--arch/ppc/amiga/config.c5
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;