summaryrefslogtreecommitdiffstats
path: root/arch/m68k/mac/via6522.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/m68k/mac/via6522.c')
-rw-r--r--arch/m68k/mac/via6522.c590
1 files changed, 590 insertions, 0 deletions
diff --git a/arch/m68k/mac/via6522.c b/arch/m68k/mac/via6522.c
new file mode 100644
index 000000000..d2a25ab13
--- /dev/null
+++ b/arch/m68k/mac/via6522.c
@@ -0,0 +1,590 @@
+
+/*
+ * 6522 Versatile Interface Adapter (VIA)
+ *
+ * There are two of these on the Mac II. Some IRQ's are vectored
+ * via them as are assorted bits and bobs - eg rtc, adb.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include "via6522.h"
+
+volatile unsigned char *via1=(unsigned char *)VIABASE;
+volatile unsigned char *via2=(unsigned char *)VIABASE2;
+
+unsigned char via1_clock, via1_datab;
+
+static int rbv=0;
+
+/*
+ * Debugging the VBL ints
+ */
+
+extern int console_loglevel;
+
+/*
+ * VIA1 - hardwired vectors
+ */
+
+#if 0 /* gone to macints.[ch] */
+extern void via_wtf(int slot, void *via, struct pt_regs *regs);
+static void via_do_nubus(int slot, volatile void *via, struct pt_regs *regs);
+
+extern void adb_interrupt(int slot, void *via, struct pt_regs *regs);
+
+static struct via_irq_tab via1_func_tab=
+{
+ {
+ via_wtf, /* One second interrupt */
+ via_wtf, /* Vblank */
+ via_wtf, /* ADB data ready */
+ via_wtf, /* ADB data */
+ via_wtf, /* ADB clock */
+ via_wtf,
+ via_wtf, /* Slot 6 is replaced by the timer */
+ via_wtf
+ }
+};
+
+static struct via_irq_tab via2_func_tab=
+{
+ {
+ via_wtf,
+ via_do_nubus,
+ via_wtf,
+ via_wtf,
+ via_wtf,
+ via_wtf,
+ via_wtf,
+ via_wtf
+ }
+};
+
+static struct via_irq_tab nubus_func_tab=
+{
+ {
+ via_wtf,
+ via_wtf,
+ via_wtf,
+ via_wtf,
+ via_wtf,
+ via_wtf,
+ via_wtf,
+ via_wtf
+ }
+};
+#endif
+
+extern void adb_interrupt(int slot, void *via, struct pt_regs *regs);
+
+#define MAC_CLOCK_TICK (783300/HZ) /* ticks per HZ */
+#define MAC_CLOCK_LOW (MAC_CLOCK_TICK&0xFF)
+#define MAC_CLOCK_HIGH (MAC_CLOCK_TICK>>8)
+
+
+void via_init_clock(void (*func)(int, void *, struct pt_regs *))
+{
+ unsigned char c;
+
+/* mac_debugging_penguin(6);*/
+
+ switch(macintosh_config->via_type)
+ {
+ /*
+ * CI, SI, VX, LC
+ */
+ case MAC_VIA_IIci:
+ via1=(void *)0x50F00000;
+ via2=(void *)0x50F26000;
+ rbv=1;
+ break;
+ /*
+ * Quadra and early MacIIs agree on the VIA locations
+ */
+ case MAC_VIA_QUADRA:
+ case MAC_VIA_II:
+ via1=(void *)0x50F00000;
+ via2=(void *)0x50F02000;
+ break;
+ default:
+ }
+ via1_clock=via_read(via1, vACR);
+ via1_datab=via_read(via1, vBufB);
+
+ /*
+ * Tell what MacOS left us with
+ */
+
+ printk("via_init: boot via1 acr=%X pcr=%X buf_a=%X dir_a=%X buf_b=%X dir_b=%X \n",
+ (int)via1_clock, (int)via_read(via1, vPCR),
+ (int)via_read(via1, vBufA), (int)via_read(via1, vDirA),
+ (int)via_read(via1, vBufB), (int)via_read(via1, vDirB));
+
+ if (rbv == 0)
+ printk("via_init: boot via2 acr=%X pcr=%X buf_a=%X dir_a=%X buf_b=%X dir_b=%X \n",
+ (int)via_read(via2, vACR), (int)via_read(via2, vPCR),
+ (int)via_read(via2, vBufA), (int)via_read(via2, vDirA),
+ (int)via_read(via2, vBufB), (int)via_read(via2, vDirB));
+
+ /*
+ * Shut it down
+ */
+
+ via_write(via1,vIER, 0x7F);
+
+ /*
+ * Kill the timers
+ */
+
+ via_write(via1,vT1LL,0);
+ via_write(via1,vT1LH,0);
+ via_write(via1,vT1CL,0);
+ via_write(via1,vT1CH,0);
+ via_write(via1,vT2CL,0);
+ via_write(via1,vT2CH,0);
+
+ /*
+ * Now do via2
+ */
+
+ if(rbv==0)
+ {
+ via_write(via2,vT1LL,0);
+ via_write(via2,vT1LH,0);
+ via_write(via2,vT1CL,0);
+ via_write(via2,vT1CH,0);
+ via_write(via2,vT2CL,0);
+ via_write(via2,vT2CH,0);
+ via_write(via2,vIER, 0x7F);
+ }
+ else
+ {
+ /*
+ * Init the RBV chip a bit
+ */
+
+ via_write(via2, rIER,0x7F);
+ }
+
+ /*
+ * Disable the timer latches
+ */
+
+ c=via_read(via1,vACR);
+ via_write(via1,vACR,c&0x3F);
+
+ if(rbv==0)
+ {
+ c=via_read(via2,vACR);
+ via_write(via2,vACR,c&0x3F);
+ }
+
+ /*
+ * Now start the clock - we want 100Hz
+ */
+
+ via_write(via1,vACR,via_read(via1,vACR)|0x40);
+
+ via_write(via1,vT1LL, MAC_CLOCK_LOW);
+ via_write(via1,vT1LH, MAC_CLOCK_HIGH);
+ via_write(via1,vT1CL, MAC_CLOCK_LOW);
+ via_write(via1,vT1CH, MAC_CLOCK_HIGH);
+
+ /*
+ * And enable its interrupt
+ */
+
+ request_irq(IRQ_MAC_TIMER_1, func, IRQ_FLG_LOCK, "timer", func);
+
+/* mac_debugging_penguin(7);*/
+
+ /*
+ * SE/30: disable video int.
+ * XXX: testing for SE/30 VBL
+ */
+
+ if (macintosh_config->ident == MAC_MODEL_SE30
+ && console_loglevel != 10) {
+ c = via_read(via1, vBufB);
+ via_write(via1, vBufB, c|(0x40));
+ c = via_read(via1, vDirB);
+ via_write(via1, vDirB, c|(0x40));
+ }
+
+ /*
+ * XXX: use positive edge
+ */
+
+ if (console_loglevel == 10) {
+ c = via_read(via1, vPCR);
+ via_write(via1, vPCR, c|(0x1));
+ }
+
+#if 0 /* gone to mac_init_IRQ */
+ /*
+ * Set vPCR for SCSI interrupts.
+ *
+ * That is: CA1 negative edge int., CA2 indep., positive edge int.;
+ * CB1 negative edge int., CB2 indep., positive edge int..
+ */
+ via_write(via2,vPCR, 0x66);
+#endif
+
+}
+
+#if 0 /* moved to macints.c */
+
+static void via_irq(volatile unsigned char *via, struct via_irq_tab *irqtab,
+ struct pt_regs *regs)
+{
+ unsigned char events=(via_read(via, vIFR)&via_read(via,vIER))&0x7F;
+ int i;
+ int ct=0;
+
+ /*
+ * Shouldnt happen
+ */
+
+ if(events==0)
+ {
+ printk("via_irq: nothing pending!\n");
+ return;
+ }
+
+ do {
+ /*
+ * Clear the pending flag
+ */
+
+ /* HACK HACK - FIXME !!! - just testing some keyboard ideas */
+
+ /* events&=~(1<<4); */
+ via_write(via, vIFR, events);
+
+ /*
+ * Now see what bits are raised
+ */
+
+ for(i=0;i<7;i++)
+ {
+ if(events&(1<<i))
+ (irqtab->vector[i])(i, via, regs);
+ }
+
+ /*
+ * And done..
+ */
+ events=(via_read(via, vIFR)&via_read(via,vIER))&0x7F;
+ ct++;
+ if(events && ct>8)
+ {
+ printk("via: stuck events %x\n",events);
+ break;
+ }
+ }
+ while(events);
+
+ scsi_mac_polled();
+}
+
+/*
+ *
+ * The RBV is different. RBV appears to stand for randomly broken
+ * VIA.
+ */
+
+static void rbv_irq(volatile unsigned char *via, struct via_irq_tab *irqtab,
+ struct pt_regs *regs)
+{
+ unsigned char events=(via_read(via, rIFR)&via_read(via,rIER))&0x7F;
+ int i;
+ int ct=0;
+
+ /*
+ * Shouldnt happen
+ */
+
+ if(events==0)
+ {
+ printk("rbv_irq: nothing pending!\n");
+ return;
+ }
+
+ do {
+ /*
+ * Clear the pending flag
+ */
+
+ /* HACK HACK - FIXME !!! - just testing some keyboard ideas */
+
+ /* events&=~(1<<4); */
+ via_write(via, rIFR, events);
+
+ /*
+ * Now see what bits are raised
+ */
+
+ for(i=0;i<7;i++)
+ {
+ if(events&(1<<i))
+ (irqtab->vector[i])(i, via, regs);
+ }
+
+ /*
+ * And done..
+ */
+ events=(via_read(via, rIFR)&via_read(via,rIER))&0x7F;
+ ct++;
+ if(events && ct>8)
+ {
+ printk("rbv: stuck events %x\n",events);
+ for(i=0;i<7;i++)
+ {
+ if(events&(1<<i))
+ {
+ printk("rbv - bashing source %d\n",
+ i);
+ via_write(via, rIER, i);
+ via_write(via, rIFR, i);
+ }
+ }
+ break;
+ }
+ }
+ while(events);
+}
+
+/*
+ * System interrupts
+ */
+
+void via1_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ via_irq(via1, &via1_func_tab, regs);
+}
+
+/*
+ * Nubus interrupts
+ */
+
+void via2_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+/* printk("via2 interrupt\n");*/
+ if(rbv)
+ rbv_irq(via2, &via2_func_tab, regs);
+ else
+ via_irq(via2, &via2_func_tab, regs);
+}
+
+/*
+ * Unexpected via interrupt
+ */
+
+void via_wtf(int slot, volatile void *via, struct pt_regs *regs)
+{
+ printk("Unexpected event %d on via %p\n",slot,via);
+}
+
+void nubus_wtf(int slot, volatile void *via, struct pt_regs *regs)
+{
+ printk("Unexpected interrupt on nubus slot %d\n",slot);
+}
+
+#endif
+
+/*
+ * The power switch - yes its software!
+ */
+
+void mac_reset(void)
+{
+ if(rbv) {
+ via_write(via2, rBufB, via_read(via2, rBufB)&~0x04);
+ } else {
+ /* Direction of vDirB is output */
+ via_write(via2,vDirB,via_read(via2,vDirB)|0x04);
+ /* Send a value of 0 on that line */
+ via_write(via2,vBufB,via_read(via2,vBufB)&~0x04);
+ }
+ /* We never make it this far... */
+ /* XXX - delay do we need to spin here ? */
+ while(1); /* Just in case .. */
+}
+
+/*
+ * Set up the keyboard
+ */
+
+void via_setup_keyboard(void)
+{
+#if 0 /* moved to adb */
+ via1_func_tab.vector[2]=adb_interrupt;
+#else
+ request_irq(IRQ_MAC_ADB, adb_interrupt, IRQ_FLG_LOCK, "adb interrupt",
+ adb_interrupt);
+#endif
+}
+
+/*
+ * Floppy hook
+ */
+
+void via1_set_head(int head)
+{
+ if(head==0)
+ via_write(via1, vBufA, via_read(via1, vBufA)&~0x20);
+ else
+ via_write(via1, vBufA, via_read(via1, vBufA)|0x20);
+}
+
+#if 0 /* moved to macints.c */
+
+
+/*
+ * Set up the SCSI
+ */
+
+void via_scsi_disable(void)
+{
+ if (rbv)
+ via_write(via2, rIER, (1<<3)|(1<<0));
+ else
+ via_write(via2, vIER, (1<<3)|(1<<0));
+}
+
+void via_scsi_enable(void)
+{
+ if (rbv)
+ via_write(via2, rIER, (1<<3)|(1<<0)|0x80);
+ else
+ via_write(via2, vIER, (1<<3)|(1<<0)|0x80);
+}
+
+void via_scsi_clear(void)
+{
+ if (rbv)
+ via_write(via2, rIFR, (1<<3)|(1<<0)|0x80);
+ volatile unsigned char deep_magic=via_read(via2, vBufB);
+ via_scsi_enable();
+}
+
+void via_setup_scsi(void (*handler)(int,volatile void *,struct pt_regs *))
+{
+ via2_func_tab.vector[0]=handler; /* SCSI DRQ */
+ via2_func_tab.vector[3]=handler; /* SCSI IRQ */
+ via_write(via2, vPCR, 0x66); /* Edge direction! */
+ via_scsi_enable();
+}
+
+/*
+ * Nubus handling
+ */
+
+static int nubus_active=0;
+
+int nubus_request_irq(int slot, void (*handler)(int,void *,struct pt_regs *))
+{
+ slot-=9;
+/* printk("Nubus request irq for slot %d\n",slot);*/
+ if(nubus_func_tab.vector[slot]==nubus_wtf)
+ return -EBUSY;
+ nubus_func_tab.vector[slot]=handler;
+ nubus_active|=1<<slot;
+/* printk("program slot %d\n",slot);*/
+/* printk("via2=%p\n",via2);*/
+#if 0
+ via_write(via2, vDirA,
+ via_read(via2, vDirA)|(1<<slot));
+ via_write(via2, vBufA, 0);
+#endif
+ if (!rbv) {
+ /* Make sure the bit is an input */
+ via_write(via2, vDirA,
+ via_read(via2, vDirA)&~(1<<slot));
+ }
+/* printk("nubus irq on\n");*/
+ return 0;
+}
+
+int nubus_free_irq(int slot)
+{
+ slot-=9;
+ nubus_active&=~(1<<slot);
+ nubus_func_tab.vector[slot]=nubus_wtf;
+ if (rbv) {
+ via_write(via2, rBufA, 1<<slot);
+ } else {
+ via_write(via2, vDirA,
+ via_read(via2, vDirA)|(1<<slot));
+ via_write(via2, vBufA, 1<<slot);
+ via_write(via2, vDirA,
+ via_read(via2, vDirA)&~(1<<slot));
+ }
+ return 0;
+}
+
+static void via_do_nubus(int slot, volatile void *via, struct pt_regs *regs)
+{
+ unsigned char map;
+ int i;
+ int ct=0;
+
+/* printk("nubus interrupt\n");*/
+
+ if (rbv) {
+ via_write(via2, rIFR, 0x82); /* lock the nubus interrupt */
+ } else {
+ via_write(via2, vIFR, 0x82); /* lock the nubus interrupt */
+
+ while(1)
+ {
+ if(rbv)
+ map=~via_read(via2, rBufA);
+ else
+ map=~via_read(via2, vBufA);
+ if((map=(map&nubus_active))==0)
+ break;
+ if(ct++>2)
+ {
+ printk("nubus stuck events - %d/%d\n", map, nubus_active);
+ return;
+ }
+ for(i=0;i<7;i++)
+ {
+ if(map&(1<<i))
+ {
+ (nubus_func_tab.vector[i])(i+9, via, regs);
+ }
+ }
+ if (rbv)
+ via_write(via2, rIFR, 0x02); /* clear it */
+ else
+ via_write(via2, vIFR, 0x02); /* clear it */
+ }
+
+ /* And done */
+}
+#endif
+
+void nubus_init_via(void)
+{
+ if (rbv) {
+ via_write(via2, rBufB, via_read(via2, rBufB)|0x02);
+ via_write(via2, rIER, 0x82); /* Interrupts on */
+ } else {
+ /* Assert the nubus active */
+ via_write(via2, vDirB, via_read(via2, vDirB)|0x02);
+ via_write(via2, vBufB, via_read(via2, vBufB)|0x02);
+ /* Make the nubus interrupt source register all output (disable) */
+ /* via_write(via2, vDirA, 0xFF); */
+ via_write(via2, vIER, 0x82); /* Interrupts on */
+ }
+ printk("BTW boot via1 acr=%X datab=%X pcr=%X\n",
+ (int)via1_clock, (int)via1_datab, (int)via_read(via1, vPCR));
+}
+