]> git.neil.brown.name Git - history.git/commitdiff
Import 2.3.45pre2 2.3.45pre2
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:31:24 +0000 (15:31 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:31:24 +0000 (15:31 -0500)
51 files changed:
arch/i386/defconfig
arch/m68k/config.in
arch/m68k/kernel/Makefile
arch/m68k/kernel/entry.S
arch/m68k/kernel/m68k_ksyms.c
arch/m68k/mac/Makefile
arch/m68k/mac/adb-bus.c [deleted file]
arch/m68k/mac/baboon.c [new file with mode: 0644]
arch/m68k/mac/config.c
arch/m68k/mac/iop.c
arch/m68k/mac/macints.c
arch/m68k/mac/mackeyb.c [deleted file]
arch/m68k/mac/misc.c [new file with mode: 0644]
arch/m68k/mac/oss.c
arch/m68k/mac/psc.c
arch/m68k/mac/via.c
arch/m68k/mm/motorola.c
arch/m68k/mm/sun3mmu.c
drivers/char/Makefile
drivers/char/amikeyb.c
drivers/char/amiserial.c [new file with mode: 0644]
drivers/char/mac_SCC.c [deleted file]
drivers/char/mac_SCC.h [deleted file]
drivers/macintosh/Makefile
drivers/macintosh/adb-iop.c [new file with mode: 0644]
drivers/macintosh/via-macii.c [new file with mode: 0644]
drivers/macintosh/via-maciisi.c [new file with mode: 0644]
drivers/macintosh/via-pmu68k.c [new file with mode: 0644]
drivers/net/ariadne.c
drivers/net/hydra.c
drivers/scsi/mac_esp.c
drivers/scsi/mac_scsi.c
drivers/scsi/mvme16x.c
drivers/scsi/sun3_NCR5380.c [new file with mode: 0644]
drivers/scsi/sun3_scsi.c
drivers/scsi/sun3_scsi.h
drivers/video/Config.in
drivers/video/Makefile
drivers/video/bwtwofb.c
drivers/video/fbcon-mac.c
drivers/video/fbmem.c
drivers/video/macfb.c
drivers/video/sun3fb.c [new file with mode: 0644]
include/asm-i386/pgtable.h
include/asm-m68k/adb_iop.h [new file with mode: 0644]
include/asm-m68k/hardirq.h
include/asm-m68k/irq.h
include/asm-m68k/mac_baboon.h [new file with mode: 0644]
include/asm-m68k/macintosh.h
include/asm-m68k/macints.h
include/asm-m68k/softirq.h

index fcc962a11fd6d3f34ea490c8d74c1f47e1563b23..fcd22384ffb5537482c58da3ad0ee087ac3f39b8 100644 (file)
@@ -270,7 +270,7 @@ CONFIG_NET_ETHERNET=y
 # CONFIG_NET_VENDOR_RACAL is not set
 # CONFIG_DEPCA is not set
 # CONFIG_NET_ISA is not set
-CONFIG_NET_EISA=y
+CONFIG_NET_PCI=y
 # CONFIG_PCNET32 is not set
 # CONFIG_APRICOT is not set
 # CONFIG_CS89x0 is not set
@@ -279,6 +279,7 @@ CONFIG_NET_EISA=y
 # CONFIG_DGRS is not set
 CONFIG_EEXPRESS_PRO100=y
 # CONFIG_NE2K_PCI is not set
+# CONFIG_8139TOO is not set
 # CONFIG_SIS900 is not set
 # CONFIG_TLAN is not set
 # CONFIG_VIA_RHINE is not set
@@ -453,7 +454,6 @@ CONFIG_EXT2_FS=y
 # CONFIG_SYSV_FS is not set
 # CONFIG_SYSV_FS_WRITE is not set
 # CONFIG_UDF_FS is not set
-# CONFIG_UDF_RW is not set
 # CONFIG_UFS_FS is not set
 # CONFIG_UFS_FS_WRITE is not set
 
index ce8260504e50b2a2abef34c4fc0f3cc547aaca1f..5b12f470eb22e572df92360e6ad3a94411f767b0 100644 (file)
@@ -161,10 +161,17 @@ if [ "$CONFIG_SCSI" != "n" ]; then
    comment 'SCSI support type (disk, tape, CD-ROM)'
 
    dep_tristate '  SCSI disk support' CONFIG_BLK_DEV_SD $CONFIG_SCSI
+   if [ "$CONFIG_BLK_DEV_SD" != "n" ]; then
+      int  'Maximum number of SCSI disks that can be loaded as modules' CONFIG_SD_EXTRA_DEVS 40
+   fi
    dep_tristate '  SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI
+   if [ "$CONFIG_BLK_DEV_ST" != "n" ]; then
+      int  'Maximum number of SCSI tapes that can be loaded as modules' CONFIG_ST_EXTRA_DEVS 2
+   fi
    dep_tristate '  SCSI CD-ROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI
    if [ "$CONFIG_BLK_DEV_SR" != "n" ]; then
       bool '    Enable vendor-specific extensions (for SCSI CDROM)' CONFIG_BLK_DEV_SR_VENDOR
+      int  'Maximum number of CDROM devices that can be loaded as modules' CONFIG_SR_EXTRA_DEVS 2
    fi
    dep_tristate '  SCSI generic support' CONFIG_CHR_DEV_SG $CONFIG_SCSI
 
@@ -273,9 +280,13 @@ if [ "$CONFIG_NET" = "y" ]; then
         tristate '  Apollo 3c505 support' CONFIG_APOLLO_ELPLUS
       fi
       if [ "$CONFIG_MAC" = "y" ]; then
-        bool '  Mac NS 8390 based ethernet cards' CONFIG_DAYNAPORT
-#       bool '  Macintosh (AV) onboard MACE ethernet' CONFIG_MACMACE
-        bool '  Macintosh (Quadra) onboard SONIC ethernet' CONFIG_MACSONIC
+        bool '  Macintosh NS 8390 based ethernet cards' CONFIG_MAC8390
+        tristate '  Macintosh SONIC based ethernet (onboard, NuBus, LC, CS)' CONFIG_MACSONIC
+        tristate '  Macintosh SMC 9194 based ethernet cards' CONFIG_SMC9194
+        tristate '  Macintosh CS89x0 based ethernet cards' CONFIG_MAC89x0
+        if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+               bool '  Macintosh (AV) onboard MACE ethernet (EXPERIMENTAL)' CONFIG_MACMACE
+        fi
       fi
       if [ "$CONFIG_VME" = "y" -a "$CONFIG_MVME147" = "y" ]; then
         tristate '  MVME147 (Lance) Ethernet support' CONFIG_MVME147_NET
@@ -352,12 +363,6 @@ if [ "$CONFIG_ATARI" = "y" ]; then
       define_bool CONFIG_BUSMOUSE y
    fi
 fi
-if [ "$CONFIG_MAC" = "y" ]; then
-   bool 'Mac ADB mouse support' CONFIG_ADBMOUSE
-   if [ "$CONFIG_ADBMOUSE" != "n" ]; then
-      define_bool CONFIG_BUSMOUSE y
-   fi
-fi
 if [ "$CONFIG_ATARI" = "y" ]; then
    tristate 'Atari MFP serial support' CONFIG_ATARI_MFPSER
    tristate 'Atari SCC serial support' CONFIG_ATARI_SCC
@@ -384,7 +389,20 @@ if [ "$CONFIG_PARPORT" = "n" ]; then
    fi
 fi
 if [ "$CONFIG_MAC" = "y" ]; then
-   bool 'Mac SCC serial support' CONFIG_MAC_SCC
+   tristate 'Macintosh serial support' CONFIG_MAC_SCC
+   bool 'Apple Desktop Bus (ADB) support' CONFIG_ADB
+   if [ "$CONFIG_ADB" = "y" ]; then
+      bool '   Support for ADB keyboard' CONFIG_ADB_KEYBOARD 
+      bool '   Support for ADB mouse' CONFIG_ADBMOUSE
+      bool '   Include Mac II ADB driver' CONFIG_ADB_MACII
+      bool '   Include Mac IIsi ADB driver' CONFIG_ADB_MACIISI
+      bool '   Include CUDA ADB driver' CONFIG_ADB_CUDA
+      bool '   Include IOP (IIfx/Quadra 9x0) ADB driver' CONFIG_ADB_IOP
+      bool '   Include PMU (Powerbook) ADB driver' CONFIG_ADB_PMU68K
+   fi
+   if [ "$CONFIG_ADBMOUSE" = "y" ]; then
+      define_bool CONFIG_BUSMOUSE y
+   fi
 fi
 if [ "$CONFIG_HP300" = "y" -a "$CONFIG_DIO" = "y" ]; then
    tristate 'HP DCA serial support' CONFIG_HPDCA
index 6d10ca35f1672384460fb25612d5ef3c4526707d..c51e43802d496b283850490509b7c8783aa0d732 100644 (file)
@@ -27,6 +27,8 @@ endif
 
 head.o: head.S m68k_defs.h
 
+entry.o: entry.S m68k_defs.h
+
 sun3-head.o: sun3-head.S m68k_defs.h
 
 m68k_defs.h: m68k_defs.c m68k_defs.head
index 02165220158fde80b8f88972f13b5f701be5182e..5f08ef216d89d89ee6d29f65999e4f96bdc57295 100644 (file)
@@ -236,12 +236,12 @@ SYMBOL_NAME_LABEL(ret_from_interrupt)
 #endif
        /* check if we need to do software interrupts */
 
-       movel   SYMBOL_NAME(bh_active),%d0
-       andl    SYMBOL_NAME(bh_mask),%d0
+       movel   SYMBOL_NAME(softirq_state),%d0
+       andl    SYMBOL_NAME(softirq_state)+4,%d0
        jeq     SYMBOL_NAME(ret_from_exception)
 
        pea     SYMBOL_NAME(ret_from_exception)
-       jra     SYMBOL_NAME(do_bottom_half)
+       jra     SYMBOL_NAME(do_softirq)
 
 
 /* Handler for uninitialized and spurious interrupts */
index 790d153abc55ba442e35ade47260767862a294cb..6f8300422e4930c9bee595db58318701bde2aef2 100644 (file)
@@ -18,7 +18,6 @@
 #include <asm/checksum.h>
 #include <asm/hardirq.h>
 #include <asm/softirq.h>
-#include <asm/m68kserial.h>
 
 asmlinkage long long __ashrdi3 (long long, int);
 asmlinkage long long __lshrdi3 (long long, int);
index 10613c0a4c7f35968b4b9d3681f682eecfa24ff5..f50cd262c9c5d66c4da28e8f0b64f75598f3154a 100644 (file)
@@ -10,6 +10,6 @@
 O_TARGET := mac.o
 OX_OBJS  := mac_ksyms.o
 O_OBJS  := config.o bootparse.o macints.o iop.o via.o oss.o psc.o \
-               macboing.o debug.o misc.o
+               baboon.o macboing.o debug.o misc.o
 
 include $(TOPDIR)/Rules.make
diff --git a/arch/m68k/mac/adb-bus.c b/arch/m68k/mac/adb-bus.c
deleted file mode 100644 (file)
index 23e98c0..0000000
+++ /dev/null
@@ -1,2699 +0,0 @@
-/*
- *     MACII   ADB keyboard handler.
- *             Copyright (c) 1997 Alan Cox
- *
- *     Derived from code 
- *             Copyright (C) 1996 Paul Mackerras.
- *
- *     MSch (9/97) Partial rewrite of interrupt handler to MacII style
- *             ADB handshake, based on:
- *                     - Guide to Mac Hardware
- *                     - Guido Koerber's session with a logic analyzer
- *
- *     MSch (1/98) Integrated start of IIsi driver by Robert Thompson
- */
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/miscdevice.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <linux/mm.h>
-#include "via6522.h"
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/adb.h>
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <asm/setup.h>
-#include <asm/macintosh.h>
-#include <asm/macints.h>
-
-
-#define MACII          /* For now - will be a switch */
-
-/* Bits in B data register: all active low */
-#define TREQ           0x08            /* Transfer request (input) */
-#define TACK           0x10            /* Transfer acknowledge (output) */
-#define TIP            0x20            /* Transfer in progress (output) */
-
-/* Bits in B data register: ADB transaction states MacII */
-#define ST_MASK                0x30            /* mask for selecting ADB state bits */
-/* ADB transaction states according to GMHW */
-#define ST_CMD         0x00            /* ADB state: command byte */
-#define ST_EVEN                0x10            /* ADB state: even data byte */
-#define ST_ODD         0x20            /* ADB state: odd data byte */
-#define ST_IDLE                0x30            /* ADB state: idle, nothing to send */
-
-/* Bits in ACR */
-#define SR_CTRL                0x1c            /* Shift register control bits */
-#ifdef USE_ORIG
-#define SR_EXT         0x1c            /* Shift on external clock */
-#else
-#define SR_EXT         0x0c            /* Shift on external clock */
-#endif
-#define SR_OUT         0x10            /* Shift out if 1 */
-
-/* Bits in IFR and IER */
-#define IER_SET                0x80            /* set bits in IER */
-#define IER_CLR                0               /* clear bits in IER */
-#define SR_INT         0x04            /* Shift register full/empty */
-#define SR_DATA                0x08            /* Shift register data */
-#define SR_CLOCK       0x10            /* Shift register clock */
-
-/* JRT */
-#define ADB_DELAY 150
-
-static struct adb_handler {
-    void (*handler)(unsigned char *, int, struct pt_regs *);
-} adb_handler[16];
-
-static enum adb_state {
-    idle,
-    sent_first_byte,
-    sending,
-    reading,
-    read_done,
-    awaiting_reply
-} adb_state;
-
-static struct adb_request *current_req;
-static struct adb_request *last_req;
-static unsigned char cuda_rbuf[16];
-static unsigned char *reply_ptr;
-static int reply_len;
-static int reading_reply;
-static int data_index;
-static int first_byte;
-static int prefix_len;
-
-static int status = ST_IDLE|TREQ;
-static int last_status;
-
-static int driver_running = 0;
-
-/*static int adb_delay;*/
-int in_keybinit = 1;
-
-static void adb_start(void);
-extern void adb_interrupt(int irq, void *arg, struct pt_regs *regs);
-extern void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs);
-extern void adb_clock_interrupt(int irq, void *arg, struct pt_regs *regs);
-extern void adb_data_interrupt(int irq, void *arg, struct pt_regs *regs);
-static void adb_input(unsigned char *buf, int nb, struct pt_regs *regs);
-
-static void adb_hw_setup_IIsi(void);
-static void adb_hw_setup_cuda(void);
-
-/*
- * debug level 10 required for ADB logging (should be && debug_adb, ideally)
- */
-
-extern int console_loglevel;
-
-/*
- * Misc. defines for testing - should go to header :-(
- */
-
-#define ADBDEBUG_STATUS                (1)
-#define ADBDEBUG_STATE         (2)
-#define ADBDEBUG_READ          (4)
-#define ADBDEBUG_WRITE         (8)
-#define ADBDEBUG_START         (16)
-#define ADBDEBUG_RETRY         (32)
-#define ADBDEBUG_POLL          (64)
-#define ADBDEBUG_INT           (128)
-#define ADBDEBUG_PROT          (256)
-#define ADBDEBUG_SRQ           (512)
-#define ADBDEBUG_REQUEST       (1024)
-#define ADBDEBUG_INPUT         (2048)
-#define ADBDEBUG_DEVICE                (4096)
-
-#define ADBDEBUG_IISI          (8192)
-
-
-/*#define DEBUG_ADB*/
-
-#ifdef DEBUG_ADB
-#define ADBDEBUG       (ADBDEBUG_INPUT | ADBDEBUG_READ | ADBDEBUG_START | ADBDEBUG_WRITE | ADBDEBUG_SRQ | ADBDEBUG_REQUEST)
-#else
-#define ADBDEBUG       (0)
-#endif
-
-#define TRY_CUDA
-
-void adb_bus_init(void)
-{
-       unsigned long flags;
-       unsigned char c, i;
-       
-       save_flags(flags);
-       cli();
-       
-       /*
-        *      Setup ADB
-        */
-        
-       switch(macintosh_config->adb_type)
-       {
-       
-               case MAC_ADB_II:
-                       printk("adb: MacII style keyboard/mouse driver.\n");
-                       /* Set the lines up. We want TREQ as input TACK|TIP as output */
-                       via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ));
-                       /*
-                        * Docs suggest TREQ should be output - that seems nuts
-                        * BSD agrees here :-)
-                        * Setup vPCR ??
-                        */
-
-#ifdef USE_ORIG
-                       /* Lower the bus signals (MacII is active low it seems ???) */
-                       via_write(via1, vBufB, via_read(via1, vBufB)&~TACK);
-#else
-                       /* Corresponding state: idle (clear state bits) */
-                       via_write(via1, vBufB, (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
-                       last_status = (via_read(via1, vBufB)&~ST_MASK);
-#endif
-                       /* Shift register on input */
-                       c=via_read(via1, vACR);
-                       c&=~SR_CTRL;            /* Clear shift register bits */
-                       c|=SR_EXT;              /* Shift on external clock; out or in? */
-                       via_write(via1, vACR, c);
-                       /* Wipe any pending data and int */
-                       via_read(via1, vSR);
-
-                       /* This is interrupts on enable SR for keyboard */
-                       via_write(via1, vIER, IER_SET|SR_INT); 
-                       /* This clears the interrupt bit */
-                       via_write(via1, vIFR, SR_INT);
-
-                       /*
-                        *      Ok we probably ;) have a ready to use adb bus. Its also
-                        *      hopefully idle (Im assuming the mac didnt leave a half
-                        *      complete transaction on booting us).
-                        */
-                       request_irq(IRQ_MAC_ADB, adb_interrupt, IRQ_FLG_LOCK, 
-                                   "adb interrupt", adb_interrupt);
-                       adb_state = idle;
-                       break;
-                       /*
-                        *      Unsupported; but later code doesn't notice !!
-                        */
-               case MAC_ADB_CUDA:
-                       printk("adb: CUDA interface.\n");
-#if 0
-                       /* don't know what to set up here ... */
-                       adb_state = idle;
-                       /* Set the lines up. We want TREQ as input TACK|TIP as output */
-                       via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ));
-#endif
-                       adb_hw_setup_cuda();
-                       adb_state = idle;
-                       request_irq(IRQ_MAC_ADB, adb_cuda_interrupt, IRQ_FLG_LOCK, 
-                                   "adb CUDA interrupt", adb_cuda_interrupt);
-                       break;
-               case MAC_ADB_IISI:
-                       printk("adb: Using IIsi hardware.\n");
-                       printk("\tDEBUG_JRT\n");
-                       /* Set the lines up. We want TREQ as input TACK|TIP as output */
-                       via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ));
-
-                       /*
-                        * MSch: I'm pretty sure the setup is mildly wrong
-                        * for the IIsi. 
-                        */
-                       /* Initial state: idle (clear state bits) */
-                       via_write(via1, vBufB, (via_read(via1, vBufB) & ~(TIP|TACK)) );
-                       last_status = (via_read(via1, vBufB)&~ST_MASK);
-                       /* Shift register on input */
-                       c=via_read(via1, vACR);
-                       c&=~SR_CTRL;            /* Clear shift register bits */
-                       c|=SR_EXT;              /* Shift on external clock; out or in? */
-                       via_write(via1, vACR, c);
-                       /* Wipe any pending data and int */
-                       via_read(via1, vSR);
-
-                       /* This is interrupts on enable SR for keyboard */
-                       via_write(via1, vIER, IER_SET|SR_INT); 
-                       /* This clears the interrupt bit */
-                       via_write(via1, vIFR, SR_INT);
-
-                       /* get those pesky clock ticks we missed while booting */
-                       for ( i = 0; i < 60; i++) {
-                               udelay(ADB_DELAY);
-                               adb_hw_setup_IIsi();
-                               udelay(ADB_DELAY);
-                               if (via_read(via1, vBufB) & TREQ)
-                                       break;
-                       }
-                       if (i == 60)
-                               printk("adb_IIsi: maybe bus jammed ??\n");
-
-                       /*
-                        *      Ok we probably ;) have a ready to use adb bus. Its also
-                        */
-                       request_irq(IRQ_MAC_ADB, adb_cuda_interrupt, IRQ_FLG_LOCK, 
-                                   "adb interrupt", adb_cuda_interrupt);
-                       adb_state = idle;
-                       break;
-               default:
-                       printk("adb: Unknown hardware interface.\n");
-               nosupp:
-                       printk("adb: Interface unsupported.\n");
-                       restore_flags(flags);   
-                       return;
-       }
-
-       /*
-        * XXX: interrupt only registered if supported HW !!
-        * -> unsupported HW will just time out on keyb_init!
-        */
-#if 0
-       request_irq(IRQ_MAC_ADB, adb_interrupt, IRQ_FLG_LOCK, 
-                   "adb interrupt", adb_interrupt);
-#endif
-#ifdef DEBUG_ADB_INTS
-       request_irq(IRQ_MAC_ADB_CL, adb_clock_interrupt, IRQ_FLG_LOCK, 
-                   "adb clock interrupt", adb_clock_interrupt);
-       request_irq(IRQ_MAC_ADB_SD, adb_data_interrupt, IRQ_FLG_LOCK, 
-                   "adb data interrupt", adb_data_interrupt);
-#endif
-
-       printk("adb: init done.\n");
-       restore_flags(flags);   
-} 
-       
-void adb_hw_setup_cuda(void)
-{
-       int             x;
-       unsigned long   flags;
-
-       printk("CUDA: HW Setup:");
-
-       save_flags(flags);
-       cli();
-
-       if (console_loglevel == 10) 
-               printk("  1,");
-
-       /* Set the direction of the cuda signals, TIP+TACK are output TREQ is an input */
-       via_write( via1, vDirB, via_read( via1, vDirB ) | TIP | TACK );
-       via_write( via1, vDirB, via_read( via1, vDirB ) & ~TREQ );
-
-       if (console_loglevel == 10) 
-               printk("2,");
-
-       /* Set the clock control. Set to shift data in by external clock CB1 */
-       via_write( via1, vACR, ( via_read(via1, vACR ) | SR_EXT ) & ~SR_OUT );
-
-       if (console_loglevel == 10) 
-               printk("3,");
-
-       /* Clear any possible Cuda interrupt */
-       x = via_read( via1, vSR );
-
-       if (console_loglevel == 10) 
-               printk("4,");
-
-       /* Terminate transaction and set idle state */
-       via_write( via1, vBufB, via_read( via1, vBufB ) | TIP | TACK );
-
-       if (console_loglevel == 10) 
-               printk("5,");
-
-       /* Delay 4 mS for ADB reset to complete */
-       udelay(4000);
-
-       if (console_loglevel == 10) 
-               printk("6,");
-
-       /* Clear pending interrupts... */
-       x = via_read( via1, vSR );
-
-       if (console_loglevel == 10) 
-               printk("7,");
-       /* Issue a sync transaction, TACK asserted while TIP negated */
-       via_write( via1, vBufB, via_read( via1, vBufB ) & ~TACK );
-
-       if (console_loglevel == 10) 
-               printk("8,");
-
-       /* Wait for the sync acknowledgement, Cuda to assert TREQ */
-       while( ( via_read( via1, vBufB ) & TREQ ) != 0 )
-               barrier();
-
-       if (console_loglevel == 10) 
-               printk("9,");
-
-       /* Wait for the sync acknowledment interrupt */
-       while( ( via_read( via1, vIFR ) & SR_INT ) == 0 )
-               barrier();
-
-       if (console_loglevel == 10) 
-               printk("10,");
-
-       /* Clear pending interrupts... */
-       x = via_read( via1, vSR );
-
-       if (console_loglevel == 10) 
-               printk("11,");
-
-       /* Terminate the sync cycle by negating TACK */
-       via_write( via1, vBufB, via_read( via1, vBufB ) | TACK );
-
-       if (console_loglevel == 10) 
-               printk("12,");
-
-       /* Wait for the sync termination acknowledgement, Cuda to negate TREQ */
-       while( ( via_read( via1, vBufB ) & TREQ ) == 0 )
-               barrier();
-
-       if (console_loglevel == 10) 
-               printk("13,");
-
-       /* Wait for the sync termination acknowledment interrupt */
-       while( ( via_read( via1, vIFR ) & SR_INT ) == 0 )
-               barrier();
-
-       if (console_loglevel == 10) 
-               printk("14,");
-
-       /* Terminate transaction and set idle state, TIP+TACK negate */
-       via_write( via1, vBufB, via_read( via1, vBufB ) | TIP );
-
-       if (console_loglevel == 10) 
-               printk("15 !");
-
-       /* Clear pending interrupts... */
-       x = via_read( via1, vSR );
-
-       restore_flags(flags);
-
-       printk("\nCUDA: HW Setup done!\n");
-}
-
-void adb_hw_setup_IIsi(void)
-{
-       int dummy;
-       long poll_timeout;
-
-       printk("adb_IIsi: cleanup!\n");
-
-       /* ??? */
-       udelay(ADB_DELAY);
-
-       /* disable SR int. */
-       via_write(via1, vIER, IER_CLR|SR_INT);
-       /* set SR to shift in */
-       via_write(via1, vACR, via_read(via1, vACR ) & ~SR_OUT);
-
-       /* this is required, especially on faster machines */
-       udelay(ADB_DELAY);
-
-       if (!(via_read(via1, vBufB) & TREQ)) { /* IRQ on */
-               /* start frame */
-               via_write(via1, vBufB,via_read(via1,vBufB) | TIP);
-
-               while (1) {
-                       /* poll for ADB interrupt and watch for timeout */
-                       /* if time out, keep going in hopes of not hanging the
-                        * ADB chip - I think */
-                       poll_timeout = ADB_DELAY * 5;
-                       while ( !(via_read(via1, vIFR) & SR_INT) 
-                               && (poll_timeout-- > 0) )
-                               dummy = via_read(via1, vBufB);
-
-                       dummy = via_read(via1, vSR);    /* reset interrupt flag */
-
-                       /* perhaps put in a check here that ignores all data
-                        * after the first ADB_MAX_MSG_LENGTH bytes ??? */
-
-                       /* end of frame reached ?? */
-                       if (via_read(via1, vBufB) & TREQ)
-                               break;
-
-                       /* set ACK */
-                       via_write(via1,vBufB,via_read(via1, vBufB) | TACK);
-                       /* delay */
-                       udelay(ADB_DELAY);
-                       /* clear ACK */
-                       via_write(via1,vBufB,via_read(via1, vBufB) & ~TACK);
-               }
-               /* end frame */
-               via_write(via1, vBufB,via_read(via1,vBufB) & ~TIP);
-               /* probably don't need to delay this long */
-               udelay(ADB_DELAY);
-       }
-       /* re-enable SR int. */
-       via_write(via1, vIER, IER_SET|SR_INT);
-}
-
-#define WAIT_FOR(cond, what)                           \
-    do {                                               \
-       for (x = 1000; !(cond); --x) {                  \
-           if (x == 0) {                               \
-               printk("Timeout waiting for " what);    \
-               return 0;                               \
-           }                                           \
-           __delay(100*160);                                   \
-       }                                               \
-    } while (0)
-
-/* 
- *     Construct and send an adb request 
- *     This function is the main entry point into the ADB driver from 
- *     kernel code; it takes the request data supplied and populates the
- *     adb_request structure.
- *     In order to keep this interface independent from any assumption about
- *     the underlying ADB hardware, we take requests in CUDA format here, 
- *     the ADB packet 'prefixed' with a packet type code.
- *     Non-CUDA hardware is confused by this, so we strip the packet type
- *     here depending on hardware type ...
- */
-int adb_request(struct adb_request *req, void (*done)(struct adb_request *),
-            int nbytes, ...)
-{
-       va_list list;
-       int i, start;
-
-       va_start(list, nbytes);
-
-       /*
-        * skip first byte if not CUDA 
-        */
-       if (macintosh_config->adb_type == MAC_ADB_II) {
-               start =  va_arg(list, int);
-               nbytes--;
-       }
-       req->nbytes = nbytes;
-       req->done = done;
-#if (ADBDEBUG & ADBDEBUG_REQUEST)
-       if (console_loglevel == 10)
-               printk("adb_request, data bytes: ");
-#endif
-       for (i = 0; i < nbytes; ++i) {
-               req->data[i] = va_arg(list, int);
-#if (ADBDEBUG & ADBDEBUG_REQUEST)
-               if (console_loglevel == 10)
-                       printk("%x ", req->data[i]);
-#endif
-       }
-#if (ADBDEBUG & ADBDEBUG_REQUEST)
-       if (console_loglevel == 10)
-               printk(" !\n");
-#endif
-       va_end(list);
-       /* 
-        * XXX: This might be fatal if no reply is generated (i.e. Listen) ! 
-        * Currently, the interrupt handler 'fakes' a reply on non-TALK 
-        * commands for this reason.
-        * Also, we need a CUDA_AUTOPOLL emulation here for non-CUDA 
-        * Macs, and some mechanism to remember the last issued TALK
-        * request for resending it repeatedly on timeout!
-        */
-       req->reply_expected = 1;
-       return adb_send_request(req);
-}
-
-/* 
- *     Construct an adb request for later sending
- *     This function only populates the adb_request structure, without 
- *     actually queueing it.
- *     Reason: Poll requests and Talk requests need to be handled in a way
- *     different from 'user' requests; no reply_expected is set and 
- *     Poll requests need to be placed at the head of the request queue.
- *     Using adb_request results in implicit queueing at the tail of the 
- *     request queue (duplicating the Poll) with reply_expected set.
- *     No adjustment of packet data is necessary, as this mechanisnm is not
- *     used by CUDA hardware (Autopoll used instead).
- */
-int adb_build_request(struct adb_request *req, void (*done)(struct adb_request *),
-            int nbytes, ...)
-{
-       va_list list;
-       int i;
-
-       req->nbytes = nbytes;
-       req->done = done;
-       va_start(list, nbytes);
-#if (ADBDEBUG & ADBDEBUG_REQUEST)
-       if (console_loglevel == 10)
-               printk("adb__build_request, data bytes: ");
-#endif
-       /*
-        * skip first byte if not CUDA ?
-        */
-       for (i = 0; i < nbytes; ++i) {
-               req->data[i] = va_arg(list, int);
-#if (ADBDEBUG & ADBDEBUG_REQUEST)
-               if (console_loglevel == 10)
-                       printk("%x ", req->data[i]);
-#endif
-       }
-#if (ADBDEBUG & ADBDEBUG_REQUEST)
-       if (console_loglevel == 10)
-               printk(" !\n");
-#endif
-       va_end(list);
-
-       req->reply_expected = 0;
-       return 0;
-}
-
-/*
- *     Send an ADB poll (Talk, tagged on the front of the request queue)
- */
-void adb_queue_poll(void)
-{
-       static int pod=0;
-       static int in_poll=0;
-       static struct adb_request r;
-       unsigned long flags;
-
-       if(in_poll)
-               printk("Double poll!\n");
-
-       in_poll++;
-       pod++;
-       if(pod>7) /* 15 */
-               pod=0;
-
-#if (ADBDEBUG & ADBDEBUG_POLL)
-       if (console_loglevel == 10)
-               printk("adb: Polling %d\n",pod);
-#endif
-
-       if (macintosh_config->adb_type == MAC_ADB_II)
-               /* XXX: that's a TALK, register 0, MacII version */
-               adb_build_request(&r,NULL, 1, (pod<<4|0xC));
-       else
-               /* CUDA etc. version */
-               adb_build_request(&r,NULL, 2, 0, (pod<<4|0xC));
-
-       r.reply_expected=0;
-       r.done=NULL;
-       r.sent=0;
-       r.got_reply=0;
-       r.reply_len=0;
-       save_flags(flags);
-       cli();
-       /* Poll inserted at head of queue ... */
-       r.next=current_req;
-       current_req=&r;
-       restore_flags(flags);
-       adb_start();
-       in_poll--;
-}
-
-/*
- *     Send an ADB retransmit (Talk, appended to the request queue)
- */
-void adb_retransmit(int device)
-{
-       static int in_retransmit=0;
-       static struct adb_request rt;
-       unsigned long flags;
-
-       if(in_retransmit)
-               printk("Double retransmit!\n");
-
-       in_retransmit++;
-
-#if (ADBDEBUG & ADBDEBUG_POLL)
-       if (console_loglevel == 10)
-               printk("adb: Sending retransmit: %d\n", device);
-#endif
-
-       /* MacII version */
-       adb_build_request(&rt,NULL, 1, (device<<4|0xC));
-
-       rt.reply_expected = 0;
-       rt.done           = NULL;
-       rt.sent           = 0;
-       rt.got_reply      = 0;
-       rt.reply_len      = 0;
-       rt.next           = NULL;
-
-       save_flags(flags);
-       cli();
-
-       /* Retransmit inserted at tail of queue ... */
-
-       if (current_req != NULL) 
-       {
-               last_req->next = &rt;
-               last_req = &rt;
-       }
-       else
-       {
-               current_req = &rt;
-               last_req = &rt;
-       }
-
-       /* always restart driver (send_retransmit used in place of adb_start!)*/
-
-       if (adb_state == idle)
-               adb_start();
-
-       restore_flags(flags);
-       in_retransmit--;
-}
-
-/*
- * Queue an ADB request; start ADB transfer if necessary
- */
-int adb_send_request(struct adb_request *req)
-{
-       unsigned long flags;
-
-       req->next = 0;
-       req->sent = 0;
-       req->got_reply = 0;
-       req->reply_len = 0;
-       save_flags(flags); 
-       cli();
-
-       if (current_req != NULL) 
-       {
-               last_req->next = req;
-               last_req = req;
-       }
-       else
-       {
-               current_req = req;
-               last_req = req;
-               if (adb_state == idle)
-                       adb_start();
-       }
-
-       restore_flags(flags);
-       return 0;
-}
-
-static int nclock, ndata;
-
-static int need_poll    = 0;
-static int command_byte = 0;
-static int last_reply   = 0;
-static int last_active  = 0;
-
-static struct adb_request *retry_req;
-
-/*
- * Start sending ADB packet 
- */
-static void adb_start(void)
-{
-       unsigned long flags;
-       struct adb_request *req;
-
-       /*
-        * We get here on three 'sane' conditions:
-        * 1) called from send_adb_request, if adb_state == idle
-        * 2) called from within adb_interrupt, if adb_state == idle 
-        *    (after receiving, or after sending a LISTEN) 
-        * 3) called from within adb_interrupt, if adb_state == sending 
-        *    and no reply is expected (immediate next command).
-        * Maybe we get here on SRQ as well ??
-        */
-
-       /* get the packet to send */
-       req = current_req;
-       /* assert adb_state == idle */
-       if (adb_state != idle) {
-               printk("ADB: adb_start called while driver busy (%p %x %x)!\n",
-                       req, adb_state, via_read(via1, vBufB)&(ST_MASK|TREQ));
-               return;
-       }
-       if (req == 0)
-               return;
-       save_flags(flags); 
-       cli();
-       
-#if (ADBDEBUG & ADBDEBUG_START)
-       if (console_loglevel == 10)
-               printk("adb_start: request %p ", req);
-#endif
-
-       nclock = 0;
-       ndata  = 0;
-
-       /* 
-        * IRQ signaled ?? (means ADB controller wants to send, or might 
-        * be end of packet if we were reading)
-        */
-       if ((via_read(via1, vBufB) & TREQ) == 0) 
-       {
-               switch(macintosh_config->adb_type)
-               {
-                       /*
-                        *      FIXME - we need to restart this on a timer
-                        *      or a collision at boot hangs us.
-                        *      Never set adb_state to idle here, or adb_start 
-                        *      won't be called again from send_request!
-                        *      (need to re-check other cases ...)
-                        */
-                       case MAC_ADB_CUDA:
-                               /* printk("device busy - fail\n"); */
-                               restore_flags(flags);
-                               /* a byte is coming in from the CUDA */
-                               return;
-                       case MAC_ADB_IISI:
-                               printk("adb_start: device busy - fail\n");
-                               retry_req = req;
-                               restore_flags(flags);
-                               return;
-                       case MAC_ADB_II:
-                               /*
-                                * if the interrupt handler set the need_poll
-                                * flag, it's hopefully a SRQ poll or re-Talk
-                                * so we try to send here anyway
-                                */
-                               if (!need_poll) {
-                                       printk("device busy - retry %p state %d status %x!\n", 
-                                               req, adb_state, via_read(via1, vBufB)&(ST_MASK|TREQ));
-                                       retry_req = req;
-                                       /* set ADB status here ? */
-                                       restore_flags(flags);
-                                       return;
-                               } else {
-#if (ADBDEBUG & ADBDEBUG_START)
-                                       if (console_loglevel == 10)
-                                               printk("device busy - polling; state %d status %x!\n", 
-                                                       adb_state, via_read(via1, vBufB)&(ST_MASK|TREQ));
-#endif
-                                       need_poll = 0;
-                                       break;
-                               }
-               }
-       }
-
-#if 0
-       /*
-        * Bus idle ?? Not sure about this one; SRQ might need ST_CMD here!
-        * OTOH: setting ST_CMD in the interrupt routine would make the 
-        * ADB contoller shift in before this routine starts shifting out ...
-        */
-       if ((via_read(via1, vBufB)&ST_MASK) != ST_IDLE) 
-       {
-#if (ADBDEBUG & ADBDEBUG_STATE)
-               if (console_loglevel == 10)
-                       printk("ADB bus not idle (%x), retry later!\n", 
-                               via_read(via1, vBufB)&(ST_MASK|TREQ));
-#endif
-               retry_req = req;
-               restore_flags(flags);
-               return;                                                 
-       }
-#endif
-
-       /*
-        * Another retry pending? (sanity check)
-        */
-       if (retry_req) {
-#if (ADBDEBUG & ADBDEBUG_RETRY)
-               if (console_loglevel == 10)
-               if (retry_req == req)
-                       /* new requests are appended at tail of request queue */
-                       printk("adb_start: retry %p pending ! \n", req);
-               else
-                       /* poll requests are added to the head of queue */
-                       printk("adb_start: retry %p pending, req %p (poll?) current! \n",
-                               retry_req, req);
-#endif
-               retry_req = NULL;
-       }
-
-       /*
-        * Seems OK, go for it!
-        */
-       switch(macintosh_config->adb_type)
-       {
-               case MAC_ADB_CUDA:
-                       /* store command byte (first byte is 'type' byte) */
-                       command_byte = req->data[1];
-                       /* set the shift register to shift out and send a byte */
-                       via_write(via1, vACR, via_read(via1, vACR)|SR_OUT); 
-                       via_write(via1, vSR, req->data[0]);
-                       via_write(via1, vBufB, via_read(via1, vBufB)&~TIP);
-                       break;
-               case MAC_ADB_IISI:
-                       /* store command byte (first byte is 'type' byte) */
-                       command_byte = req->data[1];
-                       /* set ADB state to 'active' */
-                       via_write(via1, vBufB, via_read(via1, vBufB) | TIP);
-                       /* switch ACK off (in case it was left on) */
-                       via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
-                       /* set the shift register to shift out and send a byte */
-                       via_write(via1, vACR, via_read(via1, vACR) | SR_OUT); 
-                       via_write(via1, vSR, req->data[0]);
-                       /* signal 'byte ready' */
-                       via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
-                       break;
-               case MAC_ADB_II:
-                       /* store command byte */
-                       command_byte = req->data[0];
-                       /* Output mode */
-                       via_write(via1, vACR, via_read(via1, vACR)|SR_OUT);
-                       /* Load data */
-                       via_write(via1, vSR, req->data[0]);
-#ifdef USE_ORIG
-                       /* Turn off TIP/TACK - this should tell the external logic to
-                          start the external shift clock */
-/*                     via_write(via1, vBufB, via_read(via1, vBufB)&~(TIP|TACK));*/
-                       via_write(via1, vBufB, via_read(via1, vBufB)|(TIP|TACK));
-#else
-                       /* set ADB state to 'command' */
-                       via_write(via1, vBufB, (via_read(via1, vBufB)&~ST_MASK)|ST_CMD);
-#endif
-                       break;
-       }
-       adb_state = sent_first_byte;
-       data_index = 1;
-#if (ADBDEBUG & ADBDEBUG_START)
-       if (console_loglevel == 10)
-               printk("sent first byte of %d: %x, (%x %x) ... ", 
-                       req->nbytes, req->data[0], adb_state, 
-                       (via_read(via1, vBufB) & (ST_MASK|TREQ)) );
-#endif
-       restore_flags(flags);
-}
-
-/*
- * Poll the ADB state (maybe obsolete now that interrupt-driven ADB runs)
- */
-void adb_poll(void)
-{
-       unsigned char c;
-       unsigned long flags;
-       save_flags(flags);
-       cli();
-       c=via_read(via1, vIFR);
-#if (ADBDEBUG & ADBDEBUG_POLL)
-#ifdef DEBUG_ADB_INTS
-       if (console_loglevel == 10) {
-               printk("adb_poll: IFR %x state %x cl %d dat %d ", 
-                       c, adb_state, nclock, ndata);
-               if (c & (SR_CLOCK|SR_DATA)) {
-                       if (c & SR_CLOCK)
-                               printk("adb clock event ");
-                       if (c & SR_DATA)
-                               printk("adb data event ");
-               }
-       }
-#else
-       if (console_loglevel == 10)
-               printk("adb_poll: IFR %x state %x ", 
-                       c, adb_state);
-#endif
-       if (console_loglevel == 10)
-               printk("\r");
-#endif
-       if (c & SR_INT)
-       {
-#if (ADBDEBUG & ADBDEBUG_POLL)
-               if (console_loglevel == 10)
-                       printk("adb_poll: adb interrupt event\n");
-#endif
-               adb_interrupt(0, 0, 0);
-       }
-       restore_flags(flags);
-}
-
-/*
- * Debugging gimmicks
- */
-void adb_clock_interrupt(int irq, void *arg, struct pt_regs *regs)
-{
-       nclock++;
-}
-
-void adb_data_interrupt(int irq, void *arg, struct pt_regs *regs)
-{
-       ndata++;
-}
-
-/*
- * The notorious ADB interrupt handler - does all of the protocol handling, 
- * except for starting new send operations. Relies heavily on the ADB 
- * controller sending and receiving data, thereby generating SR interrupts
- * for us. This means there has to be always activity on the ADB bus, otherwise
- * the whole process dies and has to be re-kicked by sending TALK requests ...
- * CUDA-based Macs seem to solve this with the autopoll option, for MacII-type
- * ADB the problem isn't solved yet (retransmit of the latest active TALK seems
- * a good choice; either on timeout or on a timer interrupt).
- *
- * The basic ADB state machine was left unchanged from the original MacII code
- * by Alan Cox, which was based on the CUDA driver for PowerMac. 
- * The syntax of the ADB status lines seems to be totally different on MacII, 
- * though. MacII uses the states Command -> Even -> Odd -> Even ->...-> Idle for
- * sending, and Idle -> Even -> Odd -> Even ->...-> Idle for receiving. Start 
- * and end of a receive packet are signaled by asserting /IRQ on the interrupt
- * line. Timeouts are signaled by a sequence of 4 0xFF, with /IRQ asserted on 
- * every other byte. SRQ is probably signaled by 3 or more 0xFF tacked on the 
- * end of a packet. (Thanks to Guido Koerber for eavesdropping on the ADB 
- * protocol with a logic analyzer!!)
- * CUDA seems to use /TIP -> /TIP | TACK -> /TIP -> /TIP | TACK ... -> TIP|TACK
- * for sending, and /TIP -> /TIP | TACK -> /TIP -> /TIP | TACK ... -> TIP for
- * receiving. No clue how timeouts are handled; SRQ seems to be sent as a 
- * separate packet. Quite a few changes have been made outside the handshake 
- * code, so I don't know if the CUDA code still behaves as before. 
- *
- * Note: As of 21/10/97, the MacII ADB part works including timeout detection
- * and retransmit (Talk to the last active device). Cleanup of code and 
- * testing of the CUDA functionality is required, though. 
- * Note2: As of 13/12/97, CUDA support is definitely broken ...
- * Note3: As of 21/12/97, CUDA works on a P475. What was broken? The assumption
- * that Q700 and Q800 use CUDA :-(
- *
- * 27/01/98: IIsi driver implemented (thanks to Robert Thompson for the 
- * initial bits). See adb_cuda_interrupts ...
- *
- * Next TODO: implementation of IIsi ADB protocol (maybe the USE_ORIG 
- * conditionals can be a start?)
- */
-void adb_interrupt(int irq, void *arg, struct pt_regs *regs)
-{
-       int x, adbdir;
-       unsigned long flags;
-       struct adb_request *req;
-
-       last_status = status;
-
-       /* prevent races due to SCSI enabling ints */
-       save_flags(flags);
-       cli();
-
-       if (driver_running) {
-               restore_flags(flags);
-               return;
-       }
-
-       driver_running = 1;
-       
-#ifdef USE_ORIG
-       status = (~via_read(via1, vBufB) & (TIP|TREQ)) | (via_read(via1, vACR) & SR_OUT);
-#else
-       if (macintosh_config->adb_type==MAC_ADB_CUDA)
-               status = (~via_read(via1, vBufB) & (TIP|TREQ)) | (via_read(via1, vACR) & SR_OUT);
-       else
-               /* status bits (0x8->0x20) and direction (0x10 ??) CLASH !! */
-               status = (via_read(via1, vBufB) & (ST_MASK|TREQ)); 
-#endif
-       adbdir = (via_read(via1, vACR) & SR_OUT);
-#if (ADBDEBUG & ADBDEBUG_INT)
-       if (console_loglevel == 10)
-               printk("adb_interrupt: state=%d status=%x last=%x direction=%x\n", 
-                       adb_state, status, last_status, adbdir);
-#endif
-
-       switch (adb_state) 
-       {
-               case idle:
-                       if(macintosh_config->adb_type==MAC_ADB_CUDA)
-                       {
-                               /* CUDA has sent us the first byte of data - unsolicited */
-                               if (status != TREQ)
-                                       printk("cuda: state=idle, status=%x\n", status);
-                               x = via_read(via1, vSR); 
-                               via_write(via1, vBufB, via_read(via1,vBufB)&~TIP);
-                       }
-                       else if(macintosh_config->adb_type==MAC_ADB_IISI)
-                       {
-                               udelay(150);
-                               /* set SR to IN (??? no byte received else) */
-                               via_write(via1, vACR,via_read(via1, vACR)&~SR_OUT); 
-                               /* signal start of frame */
-                               via_write(via1, vBufB, via_read(via1, vBufB) | TIP);
-                               /* read first byte */
-                               x = via_read(via1, vSR);
-                               first_byte = x;
-#if (ADBDEBUG & ADBDEBUG_READ)
-                               if (console_loglevel == 10)
-                                       printk("adb_macIIsi : receiving unsol. packet: %x (%x %x) ", 
-                                               x, adb_state, status);
-#endif
-                               /* ACK adb chip */
-                               via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
-                               udelay(150);
-                               via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
-                       }
-                       else if(macintosh_config->adb_type==MAC_ADB_II)
-                       {
-#if (ADBDEBUG & ADBDEBUG_STATUS)
-                               if (status == TREQ && !adbdir) 
-                                       /* that's: not IRQ, idle, input -> weird */
-                                       printk("adb_macII: idle, status=%x dir=%x\n",
-                                               status, adbdir);
-#endif
-                               x = via_read(via1, vSR);
-                               first_byte = x;
-#if (ADBDEBUG & ADBDEBUG_READ)
-                               if (console_loglevel == 10)
-                                       printk("adb_macII: receiving unsol. packet: %x (%x %x) ", 
-                                               x, adb_state, status);
-#endif
-                               /* set ADB state = even for first data byte */
-                               via_write(via1, vBufB, (via_read(via1, vBufB)&~ST_MASK)|ST_EVEN);
-                       }
-                       adb_state = reading;
-                       reply_ptr = cuda_rbuf;
-                       reply_len = 0;
-                       reading_reply = 0;
-                       prefix_len = 0;
-                       if (macintosh_config->adb_type==MAC_ADB_II) {
-                               *reply_ptr++ = ADB_PACKET;
-                               *reply_ptr++ = first_byte;
-                               *reply_ptr++ = command_byte; /*first_byte;*/
-                               reply_len    = 3;
-                               prefix_len   = 3;
-                       }
-                       break;
-
-               case awaiting_reply:
-                       if(macintosh_config->adb_type==MAC_ADB_CUDA)
-                       {
-                               /* CUDA has sent us the first byte of data of a reply */
-                               if (status != TREQ)
-                                       printk("cuda: state=awaiting_reply, status=%x\n", status);
-                               x = via_read(via1, vSR); 
-                               via_write(via1,vBufB, 
-                                       via_read(via1, vBufB)&~TIP);
-                       } 
-                       else if(macintosh_config->adb_type==MAC_ADB_IISI)
-                       {
-                               /* set SR to IN */
-                               via_write(via1, vACR,via_read(via1, vACR)&~SR_OUT); 
-                               /* signal start of frame */
-                               via_write(via1, vBufB, via_read(via1, vBufB) | TIP);
-                               /* read first byte */
-                               x = via_read(via1, vSR);
-                               first_byte = x;
-#if (ADBDEBUG & ADBDEBUG_READ)
-                               if (console_loglevel == 10)
-                                       printk("adb_macIIsi: reading reply: %x (%x %x) ",
-                                               x, adb_state, status);
-#endif
-#if 0
-                               if( via_read(via1,vBufB) & TREQ)
-                                       ending = 1;
-                               else
-                                       ending = 0;
-#endif
-                               /* ACK adb chip */
-                               via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
-                               udelay(150);
-                               via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
-                       }
-                       else if(macintosh_config->adb_type==MAC_ADB_II) 
-                       {
-                               /* handshake etc. for II ?? */
-                               x = via_read(via1, vSR);
-                               first_byte = x;
-#if (ADBDEBUG & ADBDEBUG_READ)
-                               if (console_loglevel == 10)
-                                       printk("adb_macII: reading reply: %x (%x %x) ",
-                                               x, adb_state, status);
-#endif
-                               /* set ADB state = even for first data byte */
-                               via_write(via1, vBufB, (via_read(via1, vBufB)&~ST_MASK)|ST_EVEN);
-                       }
-                       adb_state = reading;                    
-                       reply_ptr = current_req->reply;
-                       reading_reply = 1;
-                       reply_len  = 0;
-                       prefix_len = 0;
-                       if (macintosh_config->adb_type==MAC_ADB_II) {
-                               *reply_ptr++ = ADB_PACKET;
-                               *reply_ptr++ = first_byte;
-                               *reply_ptr++ = first_byte; /* should be command  byte */
-                               reply_len    = 3;
-                               prefix_len   = 3;
-                       }
-                       break;
-
-               case sent_first_byte:
-#if (ADBDEBUG & ADBDEBUG_WRITE)
-                       if (console_loglevel == 10)
-                               printk(" sending: %x (%x %x) ",
-                                       current_req->data[1], adb_state, status);
-#endif
-                       if(macintosh_config->adb_type==MAC_ADB_CUDA)
-                       {
-                               if (status == TREQ + TIP + SR_OUT) 
-                               {
-                                       /* collision */
-                                       via_write(via1, vACR,
-                                               via_read(via1, vACR)&~SR_OUT); 
-                                       x = via_read(via1, vSR); 
-                                       via_write(via1, vBufB,
-                                               via_read(via1,vBufB)|TIP|TACK); 
-                                       adb_state = idle;
-                               }
-                               else
-                               {
-                                       /* assert status == TIP + SR_OUT */
-                                       if (status != TIP + SR_OUT)
-                                               printk("cuda: state=sent_first_byte status=%x\n", status);
-                                       via_write(via1,vSR,current_req->data[1]); 
-                                       via_write(via1, vBufB,
-                                               via_read(via1, vBufB)^TACK); 
-                                       data_index = 2;
-                                       adb_state = sending;
-                               }
-                       }
-                       else if(macintosh_config->adb_type==MAC_ADB_IISI)
-                       {
-                               /* switch ACK off */
-                               via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
-                               if ( !(via_read(via1, vBufB) & TREQ) ) 
-                               {
-                                       /* collision */
-#if (ADBDEBUG & ADBDEBUG_WRITE)
-                                       if (console_loglevel == 10)
-                                               printk("adb_macIIsi: send collison, aborting!\n");
-#endif
-                                       /* set shift in */
-                                       via_write(via1, vACR,
-                                               via_read(via1, vACR)&~SR_OUT); 
-                                       /* clear SR int. */
-                                       x = via_read(via1, vSR); 
-                                       /* set ADB state to 'idle' */
-                                       via_write(via1, vBufB,
-                                               via_read(via1,vBufB) & ~(TIP|TACK)); 
-                                       adb_state = idle;
-                               }
-                               else
-                               {
-                                       /* delay */
-                                       udelay(ADB_DELAY);
-                                       /* set the shift register to shift out and send a byte */
-#if 0
-                                       via_write(via1, vACR, via_read(via1, vACR) | SR_OUT); 
-#endif
-                                       via_write(via1, vSR, current_req->data[1]);
-                                       /* signal 'byte ready' */
-                                       via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
-                                       data_index=2;                   
-                                       adb_state = sending;
-                               }
-                       }
-                       else if(macintosh_config->adb_type==MAC_ADB_II)
-                       {
-                               /* how to detect a collision here ?? */
-                               /* maybe we're already done (Talk, or Poll)? */
-                               if (data_index >= current_req->nbytes) 
-                               {
-                                       /* assert it's a Talk ?? */
-                                       if ( (command_byte&0xc) != 0xc
-                                           && console_loglevel == 10 )
-                                               printk("ADB: single byte command, no Talk: %x!\n", 
-                                                       command_byte);
-#if (ADBDEBUG & ADBDEBUG_WRITE)
-                                       if (console_loglevel == 10)
-                                               printk(" -> end (%d of %d) (%x %x)!\n",
-                                                       data_index, current_req->nbytes, adb_state, status);
-#endif
-                                       current_req->sent = 1;
-                                       if (current_req->reply_expected) 
-                                       {
-#if (ADBDEBUG & ADBDEBUG_WRITE)
-                                               if (console_loglevel == 10)
-                                                       printk("ADB: reply expected on poll!\n");
-#endif
-                                               adb_state = awaiting_reply;
-                                               reading_reply = 0;
-                                       } else {
-#if (ADBDEBUG & ADBDEBUG_WRITE)
-                                               if (console_loglevel == 10)
-                                                       printk("ADB: no reply for poll, not calling done()!\n");
-#endif
-                                               req = current_req;
-                                               current_req = req->next;
-#if 0  /* XXX Not sure about that one ... probably better enabled */
-                                               if (req->done)
-                                                       (*req->done)(req);
-#endif
-                                               adb_state = idle;
-                                               reading_reply = 0;
-                                       }
-                                       /* set to shift in */
-                                       via_write(via1, vACR,
-                                               via_read(via1, vACR) & ~SR_OUT);
-                                       x=via_read(via1, vSR);
-                                       /* set ADB state idle - might get SRQ */
-                                       via_write(via1, vBufB,
-                                               (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
-                                       break;
-                               }
-#if (ADBDEBUG & ADBDEBUG_STATUS)
-                               if(!(status==(ST_CMD|TREQ) && adbdir == SR_OUT))
-                                       printk("adb_macII: sent_first_byte, weird status=%x dir=%x\n", 
-                                       status, adbdir);
-#endif
-                               /* SR already set to shift out; send byte */
-                               via_write(via1, vSR, current_req->data[1]);
-                               /* set state to ST_EVEN (first byte was: ST_CMD) */
-                               via_write(via1, vBufB,
-                                       (via_read(via1, vBufB)&~ST_MASK)|ST_EVEN);
-                               data_index=2;                   
-                               adb_state = sending;
-                       }
-                       break;
-
-               case sending:
-                       req = current_req;
-                       if (data_index >= req->nbytes) 
-                       {
-#if (ADBDEBUG & ADBDEBUG_WRITE)
-                               if (console_loglevel == 10)
-                                       printk(" -> end (%d of %d) (%x %x)!\n",
-                                               data_index-1, req->nbytes, adb_state, status);
-#endif
-                               /* end of packet */
-                               if(macintosh_config->adb_type==MAC_ADB_CUDA)
-                               {
-                                       via_write(via1, vACR,
-                                               via_read(via1, vACR)&~SR_OUT); 
-                                       x = via_read(via1, vSR); 
-                                       via_write(via1, vBufB,
-                                               via_read(via1,vBufB)|TACK|TIP); 
-                               }
-                               else if(macintosh_config->adb_type==MAC_ADB_IISI)
-                               {
-                                       /* XXX maybe clear ACK here ??? */
-                                       /* switch ACK off */
-                                       via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
-                                       /* delay */
-                                       udelay(ADB_DELAY);
-                                       /* set the shift register to shift in */
-                                       via_write(via1, vACR, via_read(via1, vACR)|SR_OUT); 
-                                       /* clear SR int. */
-                                       x = via_read(via1, vSR); 
-                                       /* set ADB state 'idle' (end of frame) */
-                                       via_write(via1, vBufB,
-                                               via_read(via1,vBufB) & ~(TACK|TIP)); 
-                               }
-                               else if(macintosh_config->adb_type==MAC_ADB_II)
-                               {
-                                       /*
-                                        * XXX Not sure: maybe only switch to 
-                                        * input mode on Talk ??
-                                        */
-                                       /* set to shift in */
-                                       via_write(via1, vACR,
-                                               via_read(via1, vACR) & ~SR_OUT);
-                                       x=via_read(via1, vSR);
-                                       /* set ADB state idle - might get SRQ */
-                                       via_write(via1, vBufB,
-                                               (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
-                               }
-                               req->sent = 1;
-                               if (req->reply_expected) 
-                               {
-                                       /* 
-                                        * maybe fake a reply here on Listen ?? 
-                                        * Otherwise, a Listen hangs on success
-                                        */
-                                       if ( macintosh_config->adb_type==MAC_ADB_II
-                                            && ((req->data[0]&0xc) == 0xc) )
-                                               adb_state = awaiting_reply;
-                                       else if ( macintosh_config->adb_type != MAC_ADB_II
-                                                 && ( req->data[0]      == 0x0) 
-                                                 && ((req->data[1]&0xc) == 0xc) )
-                                               adb_state = awaiting_reply;
-                                       else {
-                                               /*
-                                                * Reply expected, but none
-                                                * possible -> fake reply.
-                                                * Problem: sending next command
-                                                * should probably be done 
-                                                * without setting bus to 'idle'!
-                                                * (except if no more commands)
-                                                */
-#if (ADBDEBUG & ADBDEBUG_PROT)
-                                               printk("ADB: reply expected on Listen, faking reply\n");
-#endif
-                                               /* make it look weird */
-                                               /* XXX: return reply_len -1? */
-                                               /* XXX: fake ADB header? */
-                                               req->reply[0] = req->reply[1] = req->reply[2] = 0xFF;  
-                                               req->reply_len = 3;
-                                               req->got_reply = 1;
-                                               current_req = req->next;
-                                               if (req->done)
-                                                       (*req->done)(req);
-                                               /* 
-                                                * ready with this one, run 
-                                                * next command or repeat last
-                                                * Talk (=idle on II)
-                                                */
-                                               /* set state to idle !! */
-                                               adb_state = idle;
-                                               if (current_req || retry_req)
-                                                       adb_start();
-                                       }
-                               }
-                               else
-                               {
-                                       current_req = req->next;
-                                       if (req->done)
-                                               (*req->done)(req);
-                                       /* not sure about this */
-                                       /* 
-                                        * MS: Must set idle, no new request 
-                                        *     started else !
-                                        */
-                                       adb_state = idle;
-                                       /* 
-                                        * requires setting ADB state to idle,
-                                        * maybe read a byte ! (done above)
-                                        */
-                                       if (current_req || retry_req)
-                                               adb_start();
-                               }
-                       }
-                       else 
-                       {
-#if (ADBDEBUG & ADBDEBUG_WRITE)
-                               if (console_loglevel == 10)
-                                       printk(" %x (%x %x) ",
-                                               req->data[data_index], adb_state, status);
-#endif
-                               if(macintosh_config->adb_type==MAC_ADB_CUDA)
-                               {
-                                       via_write(via1, vSR, req->data[data_index++]); 
-                                       via_write(via1, vBufB, 
-                                               via_read(via1, vBufB)^TACK); 
-                               }
-                               else if(macintosh_config->adb_type==MAC_ADB_IISI)
-                               {
-                                       /* switch ACK off */
-                                       via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
-                                       /* delay */
-                                       udelay(ADB_DELAY);
-                                       /* XXX: need to check for collision?? */
-                                       /* set the shift register to shift out and send a byte */
-#if 0
-                                       via_write(via1, vACR, via_read(via1, vACR)|SR_OUT); 
-#endif
-                                       via_write(via1, vSR, req->data[data_index++]);
-                                       /* signal 'byte ready' */
-                                       via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
-                               }
-                               else if(macintosh_config->adb_type==MAC_ADB_II)
-                               {
-                                       via_write(via1, vSR, req->data[data_index++]);
-                                       /* invert state bits, toggle ODD/EVEN */
-                                       x = via_read(via1, vBufB);
-                                       via_write(via1, vBufB,
-                                               (x&~ST_MASK)|~(x&ST_MASK));
-                               }
-                       }
-                       break;
-
-               case reading:
-
-                       /* timeout / SRQ handling for II hw */
-#ifdef POLL_ON_TIMEOUT
-                       if((reply_len-prefix_len)==3 && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0)
-#else
-                       if( (first_byte == 0xFF && (reply_len-prefix_len)==2 
-                            && memcmp(reply_ptr-2,"\xFF\xFF",2)==0) || 
-                           ((reply_len-prefix_len)==3 
-                            && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0))
-#endif
-                       {
-                               /*
-                                * possible timeout (in fact, most probably a 
-                                * timeout, since SRQ can't be signaled without
-                                * transfer on the bus).
-                                * The last three bytes seen were FF, together 
-                                * with the starting byte (in case we started
-                                * on 'idle' or 'awaiting_reply') this probably
-                                * makes four. So this is mostl likely #5!
-                                * The timeout signal is a pattern 1 0 1 0 0..
-                                * on /INT, meaning we missed it :-(
-                                */
-                               x = via_read(via1, vSR);
-                               if (x != 0xFF)
-                                       printk("ADB: mistaken timeout/SRQ!\n");
-
-                               /* 
-                                * ADB status bits: either even or odd.
-                                * adb_state: need to set 'idle' here.
-                                * Maybe saner: set 'need_poll' or 
-                                * 'need_resend' here, fall through to 
-                                * read_done ??
-                                */
-#if (ADBDEBUG & ADBDEBUG_READ)
-                               if (console_loglevel == 10)
-                                       printk(" -> read aborted: %x (%x %x)!\n", 
-                                               x, adb_state, status);
-#endif
-
-#if 0  /* XXX leave status unchanged!! - need to check this again! */
-       /* XXX Only touch status on II !!! */
-                               /* set ADB state to idle (required by adb_start()) */
-                               via_write(via1, vBufB,
-                                       (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
-#endif
-
-                               /*
-                                * What if the timeout happens on reading a 
-                                * reply ?? Assemble error reply and call 
-                                * current_request->done()? Keep request 
-                                * on queue?
-                                */
-
-                               /* prevent 'busy' in adb_start() */
-                               need_poll = 1;
-
-                               /*
-                                * Timeout: /IRQ alternates high/low during 
-                                *          4 'FF' bytes (1 0 1 0 0...)
-                                *          We're on byte 5, so we need one 
-                                *          more backlog here (TBI) ....
-                                */
-                               if ((status&TREQ) != (last_status&TREQ)) {
-#if (ADBDEBUG & ADBDEBUG_SRQ)
-                                       if (console_loglevel == 10)
-                                               printk("ADB: reply timeout, resending!\n");
-#endif
-                                       /*
-                                        * first byte received should be the 
-                                        * command byte timing out !!
-                                        */
-                                       if (first_byte != 0xff)
-                                               command_byte = first_byte;
-
-                                       /*
-                                        * compute target for retransmit: if
-                                        * last_active is set, use that one, 
-                                        * else use command_byte
-                                        */
-                                       if (last_active == -1)
-                                               last_active = (command_byte&0xf0)>>4;
-                                       adb_state = idle;
-                                       /* resend if TALK, don't poll! */
-                                       if (current_req)
-                                               adb_start();
-                                       else
-                                       /* 
-                                        * XXX: need to count the timeouts ?? 
-                                        * restart last active TALK ??
-                                        * If no current_req, reuse old one!
-                                        */
-                                               adb_retransmit(last_active);
-
-                               } else {
-                               /*
-                                * SRQ: NetBSD suggests /IRQ is asserted!?
-                                */
-                                       if (status&TREQ)
-                                               printk("ADB: SRQ signature w/o /INT!\n");
-#if (ADBDEBUG & ADBDEBUG_SRQ)
-                                       if (console_loglevel == 10)
-                                               printk("ADB: empty SRQ packet!\n");
-#endif
-                                       /* Terminate the SRQ packet and poll */
-                                       adb_state = idle;
-                                       adb_queue_poll();
-                               }
-                               /*
-                                * Leave ADB status lines unchanged (means /IRQ
-                                * will still be low when entering adb_start!)
-                                */
-                               break;
-                       }
-                       /* end timeout / SRQ handling for II hw. */
-                       if((reply_len-prefix_len)>3 && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0)
-                       {
-                               /* SRQ tacked on data packet */
-                               /* Check /IRQ here ?? */
-#if (ADBDEBUG & ADBDEBUG_SRQ)
-                               if (console_loglevel == 10)
-                                       printk("\nADB: Packet with SRQ!\n");
-#endif
-                               /* Terminate the packet (SRQ never ends) */
-                               x = via_read(via1, vSR);
-                               adb_state = read_done;
-                               reply_len -= 3;
-                               reply_ptr -= 3;
-                               need_poll = 1;
-                               /* need to continue; next byte not seen else */
-                               /* 
-                                * XXX: not at all sure here; maybe need to 
-                                * send away the reply and poll immediately?
-                                */
-                       } else {
-                               /* Sanity check */
-                               if(reply_len>15)
-                                       reply_len=0;
-                               /* read byte */
-                               *reply_ptr = via_read(via1, vSR); 
-                               x = *reply_ptr;
-#if (ADBDEBUG & ADBDEBUG_READ)
-                               if (console_loglevel == 10)
-                                       printk(" %x (%x %x) ", 
-                                               *reply_ptr, adb_state, status);
-#endif
-                               reply_ptr++;
-                               reply_len++;
-                       }
-                       /* The usual handshake ... */
-                       if (macintosh_config->adb_type==MAC_ADB_CUDA)
-                       {
-                               if (status == TIP)
-                               {
-                                       /* that's all folks */
-                                       via_write(via1, vBufB,
-                                               via_read(via1, vBufB)|TACK|TIP); 
-                                       adb_state = read_done;
-                               }
-                               else 
-                               {
-                                       /* assert status == TIP | TREQ */
-                                       if (status != TIP + TREQ)
-                                               printk("cuda: state=reading status=%x\n", status);
-                                       via_write(via1, vBufB, 
-                                               via_read(via1, vBufB)^TACK); 
-                               }
-                       }
-                       else if (macintosh_config->adb_type==MAC_ADB_IISI)
-                       {
-                               /* ACK adb chip (maybe check for end first?) */
-                               via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
-                               udelay(150);
-                               via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
-                               /* end of frame?? */
-                               if (status & TREQ)
-                               {
-#if (ADBDEBUG & ADBDEBUG_READ)
-                                       if (console_loglevel == 10)
-                                               printk("adb_IIsi: end of frame!\n");
-#endif
-                                       /* that's all folks */
-                                       via_write(via1, vBufB,
-                                               via_read(via1, vBufB) & ~(TACK|TIP)); 
-                                       adb_state = read_done;
-                                       /* maybe process read_done here?? Handshake anyway?? */
-                               }
-                       }
-                       else if (macintosh_config->adb_type==MAC_ADB_II)
-                       {
-                               /*
-                                * NetBSD hints that the next to last byte 
-                                * is sent with IRQ !! 
-                                * Guido found out it's the last one (0x0),
-                                * but IRQ should be asserted already.
-                                * Problem with timeout detection: First
-                                * transition to /IRQ might be second 
-                                * byte of timeout packet! 
-                                * Timeouts are signaled by 4x FF.
-                                */
-                               if(!(status&TREQ) && x == 0x00) /* != 0xFF */
-                               {
-#if (ADBDEBUG & ADBDEBUG_READ)
-                                       if (console_loglevel == 10)
-                                               printk(" -> read done!\n");
-#endif
-#if 0          /* XXX: we take one more byte (why?), so handshake! */
-                                       /* set ADB state to idle */
-                                       via_write(via1, vBufB,
-                                               (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE);
-#else
-                                       /* invert state bits, toggle ODD/EVEN */
-                                       x = via_read(via1, vBufB);
-                                       via_write(via1, vBufB,
-                                               (x&~ST_MASK)|~(x&ST_MASK));
-#endif
-                                       /* adjust packet length */
-                                       reply_len--;
-                                       reply_ptr--;
-                                       adb_state = read_done;
-                               }
-                               else
-                               {
-#if (ADBDEBUG & ADBDEBUG_STATUS)
-                                       if(status!=TIP+TREQ)
-                                               printk("macII_adb: state=reading status=%x\n", status);
-#endif
-                                       /* not caught: ST_CMD */
-                                       /* required for re-entry 'reading'! */
-                                       if ((status&ST_MASK) == ST_IDLE) {
-                                               /* (in)sanity check - set even */
-                                               via_write(via1, vBufB,
-                                                       (via_read(via1, vBufB)&~ST_MASK)|ST_EVEN);
-                                       } else {
-                                               /* invert state bits, toggle ODD/EVEN */
-                                               x = via_read(via1, vBufB);
-                                               via_write(via1, vBufB,
-                                                       (x&~ST_MASK)|~(x&ST_MASK));
-                                       }
-                               }
-                       }
-                       break;
-
-               case read_done:
-                       x = via_read(via1, vSR); 
-#if (ADBDEBUG & ADBDEBUG_READ)
-                       if (console_loglevel == 10)
-                               printk("ADB: read done: %x (%x %x)!\n", 
-                                       x, adb_state, status);
-#endif
-                       if (reading_reply) 
-                       {
-                               req = current_req;
-                               req->reply_len = reply_ptr - req->reply;
-                               req->got_reply = 1;
-                               current_req = req->next;
-                               if (req->done)
-                                       (*req->done)(req);
-                       }
-                       else
-                       {
-                               adb_input(cuda_rbuf, reply_ptr - cuda_rbuf, regs);
-                       }
-
-                       /*
-                        * remember this device ID; it's the latest we got a 
-                        * reply from!
-                        */
-                       last_reply = command_byte;
-                       last_active = (command_byte&0xf0)>>4;
-
-                       /*
-                        * Assert status = ST_IDLE ??
-                        */
-                       /* 
-                        * SRQ seen before, initiate poll now
-                        */
-                       if (need_poll) {
-#if (ADBDEBUG & ADBDEBUG_POLL)
-                               if (console_loglevel == 10)
-                                       printk("ADB: initiate poll!\n");
-#endif
-                               adb_state = idle;
-                               /*
-                                * set ADB status bits?? (unchanged above!)
-                                */
-                               adb_queue_poll();
-                               need_poll = 0;
-                               /* hope this is ok; queue_poll runs adb_start */
-                               break;
-                       }
-
-                       /*
-                        * /IRQ seen, so the ADB controller has data for us
-                        */
-                       if (!(status&TREQ)) 
-                       {
-                               /* set ADB state to idle */
-                               via_write(via1, vBufB,
-                                       (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE); 
-
-                               adb_state = reading;
-                               reply_ptr = cuda_rbuf;
-                               reply_len = 0;
-                               prefix_len = 0;
-                               if (macintosh_config->adb_type==MAC_ADB_II) {
-                                       *reply_ptr++ = ADB_PACKET;
-                                       *reply_ptr++ = command_byte;
-                                       reply_len    = 2;
-                                       prefix_len   = 2;
-                               }
-                               reading_reply = 0;
-                       }
-                       else 
-                       {
-                               /*
-                                * no IRQ, send next packet or wait
-                                */
-                               adb_state = idle;
-                               if (current_req)
-                                       adb_start();
-                               else
-                                       adb_retransmit(last_active);
-                       }
-                       break;
-
-               default:
-#if (ADBDEBUG & ADBDEBUG_STATE)
-                       printk("adb_interrupt: unknown adb_state %d?\n", adb_state);
-#endif
-       }
-       /* reset mutex and interrupts */
-       driver_running = 0;
-       restore_flags(flags);
-}
-
-/*
- * Restart of CUDA support: please modify this interrupt handler while 
- * working at the Quadra etc. ADB driver. We can try to merge them later, or
- * remove the CUDA stuff from the MacII handler
- *
- * MSch 27/01/98: Implemented IIsi driver based on initial code by Robert 
- * Thompson and hints from the NetBSD driver. CUDA and IIsi seem more closely
- * related than to the MacII code, so merging all three might be a bad
- * idea.
- */
-
-void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs)
-{
-       int x, status;
-       struct adb_request *req;
-       unsigned long flags;
-       
-       save_flags(flags);
-       cli();
-
-       if(macintosh_config->adb_type==MAC_ADB_CUDA)
-               status = (~via_read(via1, vBufB) & (TIP|TREQ)) | (via_read(via1, vACR) & SR_OUT);
-       else
-               status = via_read(via1, vBufB) & (TIP|TREQ);
-
-#if (ADBDEBUG & ADBDEBUG_INT)
-       if (console_loglevel == 10)
-               printk("adb_interrupt: state=%d status=%x\n", adb_state, status);
-#endif
-
-       switch (adb_state) 
-       {
-               case idle:
-                       first_byte = 0;
-                       if(macintosh_config->adb_type==MAC_ADB_CUDA)
-                       {
-#if (ADBDEBUG & ADBDEBUG_STATUS)
-                               /* CUDA has sent us the first byte of data - unsolicited */
-                               if (status != TREQ)
-                                       printk("cuda: state=idle, status=%x want=%x\n", 
-                                               status, TREQ);
-#endif
-                               x = via_read(via1, vSR); 
-#if (ADBDEBUG & ADBDEBUG_READ)
-                               if (console_loglevel == 10)
-                                       printk("adb_cuda: receiving unsol. packet: %x (%x %x) ", 
-                                               x, adb_state, status);
-#endif
-                               via_write(via1, vBufB, via_read(via1,vBufB)&~TIP);
-                       }
-                       else if(macintosh_config->adb_type==MAC_ADB_IISI)
-                       {
-                               udelay(150);
-                               /* set SR to IN */
-                               via_write(via1, vACR,via_read(via1, vACR)&~SR_OUT); 
-                               /* signal start of frame */
-                               via_write(via1, vBufB, via_read(via1, vBufB) | TIP);
-                               /* read first byte */
-                               x = via_read(via1, vSR);
-                               first_byte = x;
-#if (ADBDEBUG & ADBDEBUG_READ)
-                               if (console_loglevel == 10)
-                                       printk("adb_IIsi : receiving unsol. packet: %x (%x %x) ", 
-                                               x, adb_state, status);
-#endif
-                               /* ACK adb chip */
-                               via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
-                               udelay(150);
-                               via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
-                       }
-                       else if(macintosh_config->adb_type==MAC_ADB_II)
-                       {
-                               if (status != TREQ)
-                                       printk("adb_macII: state=idle status=%x want=%x\n",
-                                               status, TREQ);
-                               x = via_read(via1, vSR);
-                               via_write(via1, vBufB, via_read(via1, vBufB)&~(TIP|TACK));
-                       }
-                       adb_state = reading;
-                       reply_ptr = cuda_rbuf;
-                       reply_len = 0;
-                       reading_reply = 0;
-                       break;
-
-               case awaiting_reply:
-                       if(macintosh_config->adb_type==MAC_ADB_CUDA)
-                       {
-                               /* CUDA has sent us the first byte of data of a reply */
-#if (ADBDEBUG & ADBDEBUG_STATUS)
-                               if (status != TREQ)
-                                       printk("cuda: state=awaiting_reply, status=%x want=%x\n", 
-                                               status, TREQ);
-#endif
-                               x = via_read(via1, vSR); 
-#if (ADBDEBUG & ADBDEBUG_READ)
-                               if (console_loglevel == 10)
-                                       printk("adb_cuda: reading reply: %x (%x %x) ",
-                                               x, adb_state, status);
-#endif
-                               via_write(via1,vBufB, 
-                                       via_read(via1, vBufB)&~TIP);
-                       }
-                       else if(macintosh_config->adb_type==MAC_ADB_IISI)
-                       {
-                               /* udelay(150);*/
-                               /* set SR to IN */
-                               via_write(via1, vACR,via_read(via1, vACR)&~SR_OUT); 
-                               /* signal start of frame */
-                               via_write(via1, vBufB, via_read(via1, vBufB) | TIP);
-                               /* read first byte */
-                               x = via_read(via1, vSR);
-                               first_byte = x;
-#if (ADBDEBUG & ADBDEBUG_READ)
-                               if (console_loglevel == 10)
-                                       printk("adb_IIsi: reading reply: %x (%x %x) ",
-                                               x, adb_state, status);
-#endif
-#if 0
-                               if( via_read(via1,vBufB) & TREQ)
-                                       ending = 1;
-                               else
-                                       ending = 0;
-#endif
-                               /* ACK adb chip */
-                               via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
-                               udelay(150);
-                               via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
-                       }
-                       adb_state = reading;                    
-                       reply_ptr = current_req->reply;
-                       reading_reply = 1;
-                       reply_len = 0;
-                       break;
-
-               case sent_first_byte:
-                       if(macintosh_config->adb_type==MAC_ADB_CUDA)
-                       {
-#if (ADBDEBUG & ADBDEBUG_WRITE)
-                               if (console_loglevel == 10)
-                                       printk(" sending: %x (%x %x) ",
-                                               current_req->data[1], adb_state, status);
-#endif
-                               if (status == TREQ + TIP + SR_OUT) 
-                               {
-                                       /* collision */
-                                       if (console_loglevel == 10)
-                                               printk("adb_cuda: send collision!\n");
-                                       via_write(via1, vACR,
-                                               via_read(via1, vACR)&~SR_OUT); 
-                                       x = via_read(via1, vSR); 
-                                       via_write(via1, vBufB,
-                                               via_read(via1,vBufB)|TIP|TACK); 
-                                       adb_state = idle;
-                               }
-                               else
-                               {
-                                       /* assert status == TIP + SR_OUT */
-#if (ADBDEBUG & ADBDEBUG_STATUS)
-                                       if (status != TIP + SR_OUT)
-                                               printk("adb_cuda: state=sent_first_byte status=%x want=%x\n", 
-                                                       status, TIP + SR_OUT);
-#endif
-                                       via_write(via1,vSR,current_req->data[1]); 
-                                       via_write(via1, vBufB,
-                                               via_read(via1, vBufB)^TACK); 
-                                       data_index = 2;
-                                       adb_state = sending;
-                               }
-                       }
-                       else if(macintosh_config->adb_type==MAC_ADB_IISI)
-                       {
-                               /* switch ACK off */
-                               via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
-                               if ( !(via_read(via1, vBufB) & TREQ) ) 
-                               {
-                                       /* collision */
-#if (ADBDEBUG & ADBDEBUG_WRITE)
-                                       if (console_loglevel == 10)
-                                               printk("adb_macIIsi: send collison, aborting!\n");
-#endif
-                                       /* set shift in */
-                                       via_write(via1, vACR,
-                                               via_read(via1, vACR)&~SR_OUT); 
-                                       /* clear SR int. */
-                                       x = via_read(via1, vSR); 
-                                       /* set ADB state to 'idle' */
-                                       via_write(via1, vBufB,
-                                               via_read(via1,vBufB) & ~(TIP|TACK)); 
-                                       adb_state = idle;
-                               }
-                               else
-                               {
-                                       /* delay */
-                                       udelay(ADB_DELAY);
-                                       /* set the shift register to shift out and send a byte */
-#if 0
-                                       via_write(via1, vACR, via_read(via1, vACR) | SR_OUT); 
-#endif
-                                       via_write(via1, vSR, current_req->data[1]);
-                                       /* signal 'byte ready' */
-                                       via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
-                                       data_index=2;                   
-                                       adb_state = sending;
-                               }
-                       }
-                       else if(macintosh_config->adb_type==MAC_ADB_II)
-                       {
-                               if(status!=TIP+SR_OUT)
-                                       printk("adb_macII: state=send_first_byte status=%x want=%x\n", 
-                                               status, TIP+SR_OUT);
-                               via_write(via1, vSR, current_req->data[1]);
-                               via_write(via1, vBufB,
-                                       via_read(via1, vBufB)^TACK);
-                               data_index=2;                   
-                               adb_state = sending;
-                       }
-                       break;
-
-               case sending:
-                       req = current_req;
-                       if (data_index >= req->nbytes) 
-                       {
-#if (ADBDEBUG & ADBDEBUG_WRITE)
-                               if (console_loglevel == 10)
-                                       printk(" -> end (%d of %d) (%x %x)!\n",
-                                               data_index-1, req->nbytes, adb_state, status);
-#endif
-                               if(macintosh_config->adb_type==MAC_ADB_CUDA)
-                               {
-                                       via_write(via1, vACR,
-                                               via_read(via1, vACR)&~SR_OUT); 
-                                       x = via_read(via1, vSR); 
-                                       via_write(via1, vBufB,
-                                               via_read(via1,vBufB)|TACK|TIP); 
-                               }
-                               else if(macintosh_config->adb_type==MAC_ADB_IISI)
-                               {
-                                       /* XXX maybe clear ACK here ??? */
-                                       /* switch ACK off */
-                                       via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
-                                       /* delay */
-                                       udelay(ADB_DELAY);
-                                       /* set the shift register to shift in */
-                                       via_write(via1, vACR, via_read(via1, vACR)|SR_OUT); 
-                                       /* clear SR int. */
-                                       x = via_read(via1, vSR); 
-                                       /* set ADB state 'idle' (end of frame) */
-                                       via_write(via1, vBufB,
-                                               via_read(via1,vBufB) & ~(TACK|TIP)); 
-                               }
-                               else if(macintosh_config->adb_type==MAC_ADB_II)
-                               {
-                                       via_write(via1, vACR,
-                                               via_read(via1, vACR) & ~SR_OUT);
-                                       x=via_read(via1, vSR);
-                                       via_write(via1, vBufB,
-                                               via_read(via1, vBufB)|TACK|TIP);
-                               }
-                               req->sent = 1;
-                               if (req->reply_expected) 
-                               {
-                                       /* 
-                                        * maybe fake a reply here on Listen ?? 
-                                        * Otherwise, a Listen hangs on success
-                                        * CUDA+IIsi: only ADB Talk considered
-                                        * RTC/PRAM read (0x1 0x3) to follow.
-                                        */
-                                       if ( (req->data[0] == 0x0) && ((req->data[1]&0xc) == 0xc) )
-                                               adb_state = awaiting_reply;
-                                       else {
-                                               /*
-                                                * Reply expected, but none
-                                                * possible -> fake reply.
-                                                */
-#if (ADBDEBUG & ADBDEBUG_PROT)
-                                               printk("ADB: reply expected on Listen, faking reply\n");
-#endif
-                                               /* make it look weird */
-                                               /* XXX: return reply_len -1? */
-                                               /* XXX: fake ADB header? */
-                                               req->reply[0] = req->reply[1] = req->reply[2] = 0xFF;  
-                                               req->reply_len = 3;
-                                               req->got_reply = 1;
-                                               current_req = req->next;
-                                               if (req->done)
-                                                       (*req->done)(req);
-                                               /* 
-                                                * ready with this one, run 
-                                                * next command !
-                                                */
-                                               /* set state to idle !! */
-                                               adb_state = idle;
-                                               if (current_req || retry_req)
-                                                       adb_start();
-                                       }
-                               }
-                               else
-                               {
-                                       current_req = req->next;
-                                       if (req->done)
-                                               (*req->done)(req);
-                                       /* not sure about this */
-                                       adb_state = idle;
-                                       adb_start();
-                               }
-                       }
-                       else 
-                       {
-#if (ADBDEBUG & ADBDEBUG_WRITE)
-                               if (console_loglevel == 10)
-                                       printk(" %x (%x %x) ",
-                                               req->data[data_index], adb_state, status);
-#endif
-                               if(macintosh_config->adb_type==MAC_ADB_CUDA)
-                               {
-                                       via_write(via1, vSR, req->data[data_index++]); 
-                                       via_write(via1, vBufB, 
-                                               via_read(via1, vBufB)^TACK); 
-                               }
-                               else if(macintosh_config->adb_type==MAC_ADB_IISI)
-                               {
-                                       /* switch ACK off */
-                                       via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
-                                       /* delay */
-                                       udelay(ADB_DELAY);
-                                       /* XXX: need to check for collision?? */
-                                       /* set the shift register to shift out and send a byte */
-#if 0
-                                       via_write(via1, vACR, via_read(via1, vACR)|SR_OUT); 
-#endif
-                                       via_write(via1, vSR, req->data[data_index++]);
-                                       /* signal 'byte ready' */
-                                       via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
-                               }
-                               else if(macintosh_config->adb_type==MAC_ADB_II)
-                               {
-                                       via_write(via1, vSR, req->data[data_index++]);
-                                       via_write(via1, vBufB,
-                                               via_read(via1, vBufB)^TACK);
-                               }
-                       }
-                       break;
-
-               case reading:
-                       if(reply_len==3 && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0)
-                       {
-                               /* Terminate the SRQ packet */
-#if (ADBDEBUG & ADBDEBUG_SRQ)
-                               if (console_loglevel == 10)
-                                       printk("adb: Got an SRQ\n");
-#endif
-                               adb_state = idle;
-                               adb_queue_poll();
-                               break;
-                       }
-                       /* Sanity check - botched in orig. code! */
-                       if(reply_len>15) {
-                               printk("adb_cuda: reply buffer overrun!\n");
-                               /* wrap buffer */
-                               reply_len=0;
-                               if (reading_reply)
-                                       reply_ptr = current_req->reply;
-                               else
-                                       reply_ptr = cuda_rbuf;
-                       }
-                       *reply_ptr = via_read(via1, vSR); 
-#if (ADBDEBUG & ADBDEBUG_READ)
-                       if (console_loglevel == 10)
-                               printk(" %x (%x %x) ", 
-                                       *reply_ptr, adb_state, status);
-#endif
-                       reply_ptr++;
-                       reply_len++;
-                       if(macintosh_config->adb_type==MAC_ADB_CUDA)
-                       {
-                               if (status == TIP)
-                               {
-                                       /* that's all folks */
-                                       via_write(via1, vBufB,
-                                               via_read(via1, vBufB)|TACK|TIP); 
-                                       adb_state = read_done;
-                               }
-                               else 
-                               {
-                                       /* assert status == TIP | TREQ */
-#if (ADBDEBUG & ADBDEBUG_STATUS)
-                                       if (status != TIP + TREQ)
-                                               printk("cuda: state=reading status=%x want=%x\n", 
-                                                       status, TIP + TREQ);
-#endif
-                                       via_write(via1, vBufB, 
-                                               via_read(via1, vBufB)^TACK); 
-                               }
-                       }
-                       else if (macintosh_config->adb_type==MAC_ADB_IISI)
-                       {
-                               /* ACK adb chip (maybe check for end first?) */
-                               via_write(via1, vBufB, via_read(via1, vBufB) | TACK);
-                               udelay(150);
-                               via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK);
-                               /* end of frame?? */
-                               if (status & TREQ)
-                               {
-#if (ADBDEBUG & ADBDEBUG_READ)
-                                       if (console_loglevel == 10)
-                                               printk("adb_IIsi: end of frame!\n");
-#endif
-                                       /* that's all folks */
-                                       via_write(via1, vBufB,
-                                               via_read(via1, vBufB) & ~(TACK|TIP)); 
-                                       adb_state = read_done;
-                                       /* XXX maybe process read_done here?? 
-                                          Handshake anyway?? */
-                               }
-                       }
-                       if(macintosh_config->adb_type==MAC_ADB_II)
-                       {
-                               if( status == TIP)
-                               {
-                                       via_write(via1, vBufB,
-                                               via_read(via1, vBufB)|TACK|TIP);
-                                       adb_state = read_done;
-                               }
-                               else
-                               {
-#if (ADBDEBUG & ADBDEBUG_STATUS)
-                                       if(status!=TIP+TREQ)
-                                               printk("macII_adb: state=reading status=%x\n", status);
-#endif
-                                       via_write(via1, vBufB, 
-                                               via_read(via1, vBufB)^TACK);
-                               }
-                       }
-                       /* fall through for IIsi on end of frame */
-                       if (macintosh_config->adb_type != MAC_ADB_IISI
-                           || adb_state != read_done)
-                               break;
-
-               case read_done:
-                       x = via_read(via1, vSR); 
-#if (ADBDEBUG & ADBDEBUG_READ)
-                       if (console_loglevel == 10)
-                               printk("adb: read done: %x (%x %x)!\n", 
-                                       x, adb_state, status);
-#endif
-                       if (reading_reply) 
-                       {
-                               req = current_req;
-                               req->reply_len = reply_ptr - req->reply;
-                               req->got_reply = 1;
-                               current_req = req->next;
-                               if (req->done)
-                                       (*req->done)(req);
-                       }
-                       else
-                       {
-                               adb_input(cuda_rbuf, reply_ptr - cuda_rbuf, regs);
-                       }
-
-                       if (macintosh_config->adb_type==MAC_ADB_CUDA 
-                             &&   status & TREQ) 
-                       {
-                               via_write(via1, vBufB,
-                                       via_read(via1, vBufB)&~TIP); 
-                               adb_state = reading;
-                               reply_ptr = cuda_rbuf;
-                               reading_reply = 0;
-                       }
-                       else if  (macintosh_config->adb_type==MAC_ADB_IISI
-                             && !(status & TREQ))
-                       {
-                               udelay(150);
-                               via_write(via1, vBufB,
-                                       via_read(via1, vBufB) | TIP); 
-                               adb_state = reading;
-                               reply_ptr = cuda_rbuf;
-                               reading_reply = 0;
-                       }
-                       else 
-                       {
-                               adb_state = idle;
-                               adb_start();
-                       }
-                       break;
-
-               default:
-                       printk("adb_cuda_interrupt: unknown adb_state %d?\n", adb_state);
-       }
-
-       restore_flags(flags);
-
-}
-
-/*
- *     The 'reply delivery' routine; determines which device sent the 
- *     request and calls the appropriate handler. 
- *     Reply data are expected in CUDA format (again, argh...) so we need
- *     to fake this in the interrupt handler for MacII.
- *     Only one handler per device ID is currently possible.
- *     XXX: is the ID field here representing the default or real ID?
- */
-static void adb_input(unsigned char *buf, int nb, struct pt_regs *regs)
-{
-       int i, id;
-
-       switch (buf[0]) 
-       {
-               case ADB_PACKET:
-                       /* what's in buf[1] ?? */
-                       id = buf[2] >> 4;
-#if 0
-                       xmon_printf("adb packet: ");
-                       for (i = 0; i < nb; ++i)
-                               xmon_printf(" %x", buf[i]);
-                       xmon_printf(", id = %d\n", id);
-#endif
-#if (ADBDEBUG & ADBDEBUG_INPUT)
-                       if (console_loglevel == 10) {
-                               printk("adb_input: adb packet ");
-                               for (i = 0; i < nb; ++i)
-                                       printk(" %x", buf[i]);
-                               printk(", id = %d\n", id);
-                       }
-#endif
-                       if (adb_handler[id].handler != 0) 
-                       {
-                               (*adb_handler[id].handler)(buf, nb, regs);
-                       }
-                       break;
-
-               default:
-#if (ADBDEBUG & ADBDEBUG_INPUT)
-                       if (console_loglevel == 10) {
-                               printk("adb_input: data from via (%d bytes):", nb);
-                               for (i = 0; i < nb; ++i)
-                                       printk(" %.2x", buf[i]);
-                               printk("\n");
-                       }
-#endif
-       }
-}
-
-/* Ultimately this should return the number of devices with
-   the given default id. */
-
-int adb_register(int default_id,
-            void (*handler)(unsigned char *, int, struct pt_regs *))
-{
-       if (adb_handler[default_id].handler != 0)
-               panic("Two handlers for ADB device %d\n", default_id);
-       adb_handler[default_id].handler = handler;
-       return 1;
-}
-
-/*
- * /dev/adb device driver.
- */
-
-#define ADB_MAJOR      56      /* major number for /dev/adb */
-
-#define ADB_MAX_MINOR  64      /* range of ADB minors */
-#define ADB_TYPE_SHIFT 4       /* # bits for device ID/type in subdevices */
-
-#define ADB_TYPE_RAW   0       /* raw device; unbuffered */
-#define ADB_TYPE_BUFF  1       /* raw device; buffered */
-#define ADB_TYPE_COOKED        2       /* 'cooked' device */
-
-
-extern void adbdev_init(void);
-
-struct adbdev_state {
-       struct adb_request req;
-};
-
-static DECLARE_WAIT_QUEUE_HEAD(adb_wait);
-
-static int adb_wait_reply(struct adbdev_state *state, struct file *file)
-{
-       int ret = 0;
-       DECLARE_WAITQUEUE(wait,current);
-
-       __set_current_state(TASK_INTERRUPTIBLE);
-       add_wait_queue(&adb_wait, &wait);
-
-       while (!state->req.got_reply) {
-               if (file->f_flags & O_NONBLOCK) {
-                       ret = -EAGAIN;
-                       break;
-               }
-               if (signal_pending(current)) {
-                       ret = -ERESTARTSYS;
-                       break;
-               }
-               schedule();
-       }
-
-       __set_current_state(TASK_RUNNING);
-       remove_wait_queue(&adb_wait, &wait);
-
-       return ret;
-}
-
-static void adb_write_done(struct adb_request *req)
-{
-       if (!req->got_reply) {
-               req->reply_len = 0;
-               req->got_reply = 1;
-       }
-       wake_up_interruptible(&adb_wait);
-}
-
-struct file_operations *adb_raw[16];
-struct file_operations *adb_buffered[16];
-struct file_operations *adb_cooked[16];
-
-static int adb_open(struct inode *inode, struct file *file)
-{
-       int adb_type, adb_subtype;
-       struct adbdev_state *state;
-
-       if (MINOR(inode->i_rdev) > ADB_MAX_MINOR)
-               return -ENXIO;
-
-       switch (MINOR(inode->i_rdev) >> ADB_TYPE_SHIFT) {
-               case ADB_TYPE_RAW:
-                       /* see code below */
-                       break;
-               case ADB_TYPE_BUFF:
-                       /* TBI */
-                       return -ENXIO;
-               case ADB_TYPE_COOKED:
-                       /* subtypes such as kbd, mouse, ... */
-                       adb_subtype = MINOR(inode->i_rdev) & ~ADB_TYPE_SHIFT;
-                       if ((file->f_op = adb_cooked[adb_subtype]))
-                               return file->f_op->open(inode,file);
-                       else
-                               return -ENODEV;
-       }
-
-       state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL);
-       if (state == 0)
-               return -ENOMEM;
-       file->private_data = state;
-       state->req.reply_expected = 0;
-       return 0;
-}
-
-static void adb_release(struct inode *inode, struct file *file)
-{
-       struct adbdev_state *state = file->private_data;
-
-       if (state) {
-               file->private_data = NULL;
-               if (state->req.reply_expected && !state->req.got_reply)
-                       if (adb_wait_reply(state, file))
-                               return;
-               kfree(state);
-       }
-       return;
-}
-
-static int adb_lseek(struct inode *inode, struct file *file, 
-                    off_t offset, int origin)
-{
-       return -ESPIPE;
-}
-
-static int adb_read(struct inode *inode, struct file *file,
-                    char *buf, int count)
-{
-       int ret;
-       struct adbdev_state *state = file->private_data;
-
-       if (count < 2)
-               return -EINVAL;
-       if (count > sizeof(state->req.reply))
-               count = sizeof(state->req.reply);
-       ret = verify_area(VERIFY_WRITE, buf, count);
-       if (ret)
-               return ret;
-
-       if (!state->req.reply_expected)
-               return 0;
-
-       ret = adb_wait_reply(state, file);
-       if (ret)
-               return ret;
-
-       state->req.reply_expected = 0;
-       ret = state->req.reply_len;
-       copy_to_user(buf, state->req.reply, ret);
-
-       return ret;
-}
-
-static int adb_write(struct inode *inode, struct file *file,
-                     const char *buf, int count)
-{
-       int ret, i;
-       struct adbdev_state *state = file->private_data;
-
-       if (count < 2 || count > sizeof(state->req.data))
-               return -EINVAL;
-       ret = verify_area(VERIFY_READ, buf, count);
-       if (ret)
-               return ret;
-
-       if (state->req.reply_expected && !state->req.got_reply) {
-               /* A previous request is still being processed.
-                  Wait for it to finish. */
-               ret = adb_wait_reply(state, file);
-               if (ret)
-                       return ret;
-       }
-
-       state->req.nbytes = count;
-       state->req.done = adb_write_done;
-       state->req.got_reply = 0;
-       copy_from_user(state->req.data, buf, count);
-#if 0
-       switch (adb_hardware) {
-       case ADB_NONE:
-               return -ENXIO;
-       case ADB_VIACUDA:
-               state->req.reply_expected = 1;
-               cuda_send_request(&state->req);
-               break;
-       default:
-#endif
-               if (state->req.data[0] != ADB_PACKET)
-                       return -EINVAL;
-               for (i = 1; i < state->req.nbytes; ++i)
-                       state->req.data[i] = state->req.data[i+1];
-               state->req.reply_expected =
-                       ((state->req.data[0] & 0xc) == 0xc);
-               adb_send_request(&state->req);
-#if 0
-               break;
-       }
-#endif
-
-       return count;
-}
-
-static struct file_operations adb_fops = {
-       llseek:         adb_lseek,
-       read:           adb_read,
-       write:          adb_write,
-       open:           adb_open,
-       release:        adb_release,
-};
-
-int adbdev_register(int subtype, struct file_operations *fops)
-{
-       if (subtype < 0 || subtype > 15)
-               return -EINVAL;
-       if (adb_cooked[subtype])
-               return -EBUSY;
-       adb_cooked[subtype] = fops;
-       return 0;
-}
-
-int adbdev_unregister(int subtype)
-{
-       if (subtype < 0 || subtype > 15)
-               return -EINVAL;
-       if (!adb_cooked[subtype])
-               return -ENODEV;
-       adb_cooked[subtype] = NULL;
-       return 0;
-}
-
-void adbdev_init()
-{
-       if (register_chrdev(ADB_MAJOR, "adb", &adb_fops))
-               printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR);
-}
-
-
-#if 0 /* old ADB device */
-
-/*
- * Here are the file operations we export for /dev/adb.
- */
-
-#define ADB_MINOR      140     /* /dev/adb is c 10 140 */
-
-extern void adbdev_inits(void);
-
-struct adbdev_state {
-       struct adb_request req;
-};
-
-static DECLARE_WAIT_QUEUE_HEAD(adb_wait);
-
-static int adb_wait_reply(struct adbdev_state *state, struct file *file)
-{
-       int ret = 0;
-       DECLARE_WAITQUEUE(wait, current);
-
-#if (ADBDEBUG & ADBDEBUG_DEVICE)
-       printk("ADB request: wait_reply (blocking ... \n");
-#endif
-
-       __set_current_state(TASK_INTERRUPTIBLE);
-       add_wait_queue(&adb_wait, &wait);
-
-       while (!state->req.got_reply) {
-               if (file->f_flags & O_NONBLOCK) {
-                       ret = -EAGAIN;
-                       break;
-               }
-               if (signal_pending(current)) {
-                       ret = -ERESTARTSYS;
-                       break;
-               }
-               schedule();
-       }
-
-       __set_current_state(TASK_RUNNING);
-       remove_wait_queue(&adb_wait, &wait);
-
-       return ret;
-}
-
-static void adb_write_done(struct adb_request *req)
-{
-       if (!req->got_reply) {
-               req->reply_len = 0;
-               req->got_reply = 1;
-       }
-       wake_up_interruptible(&adb_wait);
-}
-
-static int adb_open(struct inode *inode, struct file *file)
-{
-       struct adbdev_state *state;
-
-       state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL);
-       if (state == 0)
-               return -ENOMEM;
-       file->private_data = state;
-       state->req.reply_expected = 0;
-       return 0;
-}
-
-static void adb_release(struct inode *inode, struct file *file)
-{
-       struct adbdev_state *state = file->private_data;
-
-       if (state) {
-               file->private_data = NULL;
-               if (state->req.reply_expected && !state->req.got_reply)
-                       if (adb_wait_reply(state, file))
-                               return;
-               kfree(state);
-       }
-       return;
-}
-
-static int adb_lseek(struct inode *inode, struct file *file,
-                    off_t offset, int origin)
-{
-       return -ESPIPE;
-}
-
-static int adb_read(struct inode *inode, struct file *file,
-                   char *buf, int count)
-{
-       int ret;
-       struct adbdev_state *state = file->private_data;
-
-       if (count < 2)
-               return -EINVAL;
-       if (count > sizeof(state->req.reply))
-               count = sizeof(state->req.reply);
-       ret = verify_area(VERIFY_WRITE, buf, count);
-       if (ret)
-               return ret;
-
-       if (!state->req.reply_expected)
-               return 0;
-
-       ret = adb_wait_reply(state, file);
-       if (ret)
-               return ret;
-
-       ret = state->req.reply_len;
-       memcpy_tofs(buf, state->req.reply, ret);
-       state->req.reply_expected = 0;
-
-       return ret;
-}
-
-static int adb_write(struct inode *inode, struct file *file,
-                    const char *buf, int count)
-{
-       int ret;
-       struct adbdev_state *state = file->private_data;
-
-       if (count < 2 || count > sizeof(state->req.data))
-               return -EINVAL;
-       ret = verify_area(VERIFY_READ, buf, count);
-       if (ret)
-               return ret;
-
-       if (state->req.reply_expected && !state->req.got_reply) {
-               /* A previous request is still being processed.
-                  Wait for it to finish. */
-               ret = adb_wait_reply(state, file);
-               if (ret)
-                       return ret;
-       }
-
-       state->req.nbytes = count;
-       state->req.done = adb_write_done;
-       memcpy_fromfs(state->req.data, buf, count);
-       state->req.reply_expected = 1;
-       state->req.got_reply = 0;
-       adb_send_request(&state->req);
-
-       return count;
-}
-
-static struct file_operations adb_fops = {
-       llseek:         adb_lseek,
-       read:           adb_read,
-       write:          adb_write,
-       open:           adb_open,
-       release:        adb_release,
-};
-
-static struct miscdevice adb_dev = {
-       ADB_MINOR,
-       "adb",
-       &adb_fops
-};
-
-void adbdev_init(void)
-{
-       misc_register(&adb_dev);
-}
-
-#endif /* old ADB device */
diff --git a/arch/m68k/mac/baboon.c b/arch/m68k/mac/baboon.c
new file mode 100644 (file)
index 0000000..a940053
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Baboon Custom IC Managment
+ *
+ * The Baboon custom IC controls the IDE, PCMCIA and media bay on the
+ * PowerBook 190. It multiplexes multiple interrupt sources onto the
+ * Nubus slot $C interrupt.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/traps.h>
+#include <asm/bootinfo.h> 
+#include <asm/macintosh.h> 
+#include <asm/macints.h> 
+#include <asm/mac_baboon.h>
+
+/* #define DEBUG_BABOON /**/
+/* #define DEBUG_IRQS /**/
+
+int baboon_present,baboon_active;
+volatile struct baboon *baboon;
+
+void baboon_irq(int, void *, struct pt_regs *);
+
+extern int console_loglevel;
+
+extern int macide_ack_intr(ide_hwif_t *);
+
+/*
+ * Baboon initialization.
+ */
+
+void __init baboon_init(void)
+{
+       if (macintosh_config->ident != MAC_MODEL_PB190) {
+               baboon = NULL;
+               baboon_present = 0;
+               return;
+       }
+
+       baboon = (struct baboon *) BABOON_BASE;
+       baboon_present = 1;
+       baboon_active = 0;
+
+       printk("Baboon detected at %p\n", baboon);
+}
+
+/*
+ * Register the Baboon interrupt dispatcher on nubus slot $C.
+ */
+
+void __init baboon_register_interrupts(void)
+{
+       request_irq(IRQ_NUBUS_C, baboon_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+                   "baboon", (void *) baboon);
+}
+
+/*
+ * Baboon interrupt handler. This works a lot like a VIA.
+ */
+
+void baboon_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+       int irq_bit,i;
+       unsigned char events;
+
+#ifdef DEBUG_IRQS
+       printk("baboon_irq: mb_control %02X mb_ifr %02X mb_status %02X active %02X\n",
+               (uint) baboon->mb_control, (uint) baboon->mb_ifr,
+               (uint) baboon->mb_status,  baboon_active);
+#endif
+
+       if (!(events = baboon->mb_ifr & 0x07)) return;
+
+       for (i = 0, irq_bit = 1 ; i < 3 ; i++, irq_bit <<= 1) {
+               if (events & irq_bit/* & baboon_active*/) {
+                       baboon_active &= ~irq_bit;
+                       mac_do_irq_list(IRQ_BABOON_0 + i, regs);
+                       baboon_active |= irq_bit;
+                       baboon->mb_ifr &= ~irq_bit;
+               }
+       }
+#if 0
+       if (baboon->mb_ifr & 0x02) macide_ack_intr(NULL);
+       /* for now we need to smash all interrupts */
+       baboon->mb_ifr &= ~events;
+#endif
+}
+
+void baboon_irq_enable(int irq) {
+       int irq_idx     = IRQ_IDX(irq);
+
+#ifdef DEBUG_IRQUSE
+       printk("baboon_irq_enable(%d)\n", irq);
+#endif
+       baboon_active |= (1 << irq_idx);
+}
+
+void baboon_irq_disable(int irq) {
+       int irq_idx     = IRQ_IDX(irq);
+
+#ifdef DEBUG_IRQUSE
+       printk("baboon_irq_disable(%d)\n", irq);
+#endif
+       baboon_active &= ~(1 << irq_idx);
+}
+
+void baboon_irq_clear(int irq) {
+       int irq_idx     = IRQ_IDX(irq);
+
+       baboon->mb_ifr &= ~(1 << irq_idx);
+}
+
+int baboon_irq_pending(int irq)
+{
+       int irq_idx     = IRQ_IDX(irq);
+
+       return baboon->mb_ifr & (1 << irq_idx);
+}
index b5057f6e8dd1549137b91bec0eaece0233d1c997..c1154b6aa1c121a6b79f33dadb84991719126f73 100644 (file)
 #include <asm/mac_oss.h>
 #include <asm/mac_psc.h>
 
-/* Offset between Unix time (1970-based) and Mac time (1904-based) */
-
-#define MAC_TIME_OFFSET 2082844800
-
-/*
- * hardware reset vector
- */
-
-static void (*rom_reset)(void);
-
 /* Mac bootinfo struct */
 
 struct mac_booter_data mac_bi_data = {0,};
@@ -76,10 +66,11 @@ extern int mackbd_init_hw(void);
 extern void mackbd_leds(unsigned int leds);
 
 /* Mac specific timer functions */
+extern void mac_gettod (int *, int *, int *, int *, int *, int *);
 extern unsigned long mac_gettimeoffset (void);
-static void mac_gettod (int *, int *, int *, int *, int *, int *);
-static int mac_hwclk (int, struct hwclk_time *);
-static int mac_set_clock_mmss (unsigned long);
+extern int mac_hwclk (int, struct hwclk_time *);
+extern int mac_set_clock_mmss (unsigned long);
+extern int mac_get_irq_list(char *);
 extern void iop_preinit(void);
 extern void iop_init(void);
 extern void via_init(void);
@@ -87,6 +78,7 @@ extern void via_init_clock(void (*func)(int, void *, struct pt_regs *));
 extern void via_flush_cache(void);
 extern void oss_init(void);
 extern void psc_init(void);
+extern void baboon_init(void);
 
 extern void (*kd_mksound)(unsigned int, unsigned int);
 extern void mac_mksound(unsigned int, unsigned int);
@@ -99,18 +91,6 @@ extern void nubus_sweep_video(void);
 extern void mac_debug_init(void);
 extern void mac_debugging_long(int, long);
 
-/* poweroff functions */
-extern void via_poweroff(void);
-extern void oss_poweroff(void);
-extern void adb_poweroff(void);
-extern void adb_hwreset(void);
-
-/* pram functions */
-extern __u32 via_read_time(void);
-extern void via_write_time(__u32);
-extern __u32 adb_read_time(void);
-extern void adb_write_time(__u32);
-
 #ifdef CONFIG_MAGIC_SYSRQ
 static char mac_sysrq_xlate[128] =
        "\000sdfghzxcv\000bqwer"                                /* 0x00 - 0x0f */
@@ -140,186 +120,6 @@ static void mac_sched_init(void (*vector)(int, void *, struct pt_regs *))
 
 extern int console_loglevel;
 
-/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
- * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
- * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
- *
- * [For the Julian calendar (which was used in Russia before 1917,
- * Britain & colonies before 1752, anywhere else before 1582,
- * and is still in use by some communities) leave out the
- * -year/100+year/400 terms, and add 10.]
- *
- * This algorithm was first published by Gauss (I think).
- *
- * WARNING: this function will overflow on 2106-02-07 06:28:16 on
- * machines were long is 32-bit! (However, as time_t is signed, we
- * will already get problems at other places on 2038-01-19 03:14:08)
- */
-static unsigned long mktime(unsigned int year, unsigned int mon,
-       unsigned int day, unsigned int hour,
-       unsigned int min, unsigned int sec)
-{
-       if (0 >= (int) (mon -= 2)) {    /* 1..12 -> 11,12,1..10 */
-               mon += 12;      /* Puts Feb last since it has leap day */
-               year -= 1;
-       }
-       return (((
-           (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
-             year*365 - 719499
-           )*24 + hour /* now have hours */
-          )*60 + min /* now have minutes */
-         )*60 + sec; /* finally seconds */
-}
-
-/*
- * This function translates seconds since 1970 into a proper date.
- *
- * Algorithm cribbed from glibc2.1, __offtime().
- */
-#define SECS_PER_MINUTE (60)
-#define SECS_PER_HOUR  (SECS_PER_MINUTE * 60)
-#define SECS_PER_DAY   (SECS_PER_HOUR * 24)
-
-static void unmktime(unsigned long time, long offset,
-                    int *yearp, int *monp, int *dayp,
-                    int *hourp, int *minp, int *secp)
-{
-        /* How many days come before each month (0-12).  */
-       static const unsigned short int __mon_yday[2][13] =
-       {
-               /* Normal years.  */
-               { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
-               /* Leap years.  */
-               { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
-       };
-       long int days, rem, y, wday, yday;
-       const unsigned short int *ip;
-
-       days = time / SECS_PER_DAY;
-       rem = time % SECS_PER_DAY;
-       rem += offset;
-       while (rem < 0) {
-               rem += SECS_PER_DAY;
-               --days;
-       }
-       while (rem >= SECS_PER_DAY) {
-               rem -= SECS_PER_DAY;
-               ++days;
-       }
-       *hourp = rem / SECS_PER_HOUR;
-       rem %= SECS_PER_HOUR;
-       *minp = rem / SECS_PER_MINUTE;
-       *secp = rem % SECS_PER_MINUTE;
-       /* January 1, 1970 was a Thursday. */
-       wday = (4 + days) % 7; /* Day in the week. Not currently used */
-       if (wday < 0) wday += 7;
-       y = 1970;
-
-#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
-#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
-#define __isleap(year) \
-  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
-
-       while (days < 0 || days >= (__isleap (y) ? 366 : 365))
-       {
-               /* Guess a corrected year, assuming 365 days per year.  */
-               long int yg = y + days / 365 - (days % 365 < 0);
-
-               /* Adjust DAYS and Y to match the guessed year.  */
-               days -= ((yg - y) * 365
-                        + LEAPS_THRU_END_OF (yg - 1)
-                        - LEAPS_THRU_END_OF (y - 1));
-               y = yg;
-       }
-       *yearp = y - 1900;
-       yday = days; /* day in the year.  Not currently used. */
-       ip = __mon_yday[__isleap(y)];
-       for (y = 11; days < (long int) ip[y]; --y)
-               continue;
-       days -= ip[y];
-       *monp = y;
-       *dayp = days + 1; /* day in the month */
-       return;
-}
-
-/*
- * Return the boot time for use in initializing the kernel clock.
- *
- * I'd like to read the hardware clock here but many machines read
- * the PRAM through ADB, and interrupts aren't initialized when this
- * is called so ADB obviously won't work.
- */
-
-static void mac_gettod(int *yearp, int *monp, int *dayp,
-                      int *hourp, int *minp, int *secp)
-{
-       /* Yes the GMT bias is backwards.  It looks like Penguin is
-           screwing up the boottime it gives us... This works for me
-           in Canada/Eastern but it might be wrong everywhere else. */
-       unmktime(mac_bi_data.boottime, -mac_bi_data.gmtbias * 60,
-               yearp, monp, dayp, hourp, minp, secp);
-       /* For some reason this is off by one */
-       *monp = *monp + 1;
-}
-
-/* 
- * Read/write the hardware clock.
- */
-
-static int mac_hwclk(int op, struct hwclk_time *t)
-{
-       unsigned long now;
-
-       if (!op) { /* read */
-               if (macintosh_config->adb_type == MAC_ADB_II) {
-                       now = via_read_time();
-               } else if ((macintosh_config->adb_type == MAC_ADB_IISI) ||
-                          (macintosh_config->adb_type == MAC_ADB_CUDA)) {
-                       now = adb_read_time();
-               } else if (macintosh_config->adb_type == MAC_ADB_IOP) {
-                       now = via_read_time();
-               } else {
-                       now = MAC_TIME_OFFSET;
-               }
-
-               now -= MAC_TIME_OFFSET;
-
-               t->wday = 0;
-               unmktime(now, 0,
-                        &t->year, &t->mon, &t->day,
-                        &t->hour, &t->min, &t->sec);
-       } else { /* write */
-               now = mktime(t->year + 1900, t->mon + 1, t->day,
-                            t->hour, t->min, t->sec) + MAC_TIME_OFFSET;
-
-               if (macintosh_config->adb_type == MAC_ADB_II) {
-                       via_write_time(now);
-               } else if ((macintosh_config->adb_type == MAC_ADB_IISI) ||
-                          (macintosh_config->adb_type == MAC_ADB_CUDA)) {
-                       adb_write_time(now);
-               } else if (macintosh_config->adb_type == MAC_ADB_IOP) {
-                       via_write_time(now);
-               }
-       }
-       return 0;
-}
-
-/*
- * Set minutes/seconds in the hardware clock
- */
-
-static int mac_set_clock_mmss (unsigned long nowtime)
-{
-       struct hwclk_time now;
-
-       mac_hwclk(0, &now);
-       now.sec = nowtime % 60;
-       now.min = (nowtime / 60) % 60;
-       mac_hwclk(1, &now);
-
-       return 0;
-}
-
 #if 0
 void mac_waitbut (void)
 {
@@ -330,9 +130,23 @@ void mac_waitbut (void)
 extern struct consw fb_con;
 extern struct fb_info *mac_fb_init(long *);
 
-    /*
-     *  Parse a Macintosh-specific record in the bootinfo
-     */
+extern void mac_default_handler(int, void *, struct pt_regs *);
+
+void (*mac_handlers[8])(int, void *, struct pt_regs *)=
+{
+       mac_default_handler,
+       mac_default_handler,
+       mac_default_handler,
+       mac_default_handler,
+       mac_default_handler,
+       mac_default_handler,
+       mac_default_handler,
+       mac_default_handler
+};
+
+/*
+ * Parse a Macintosh-specific record in the bootinfo
+ */
 
 int __init mac_parse_bootinfo(const struct bi_record *record)
 {
@@ -384,9 +198,9 @@ int __init mac_parse_bootinfo(const struct bi_record *record)
 }
 
 /*
- *     Flip into 24bit mode for an instant - flushes the L2 cache card. We
- *     have to disable interrupts for this. Our IRQ handlers will crap 
- *     themselves if they take an IRQ in 24bit mode!
+ * Flip into 24bit mode for an instant - flushes the L2 cache card. We
+ * have to disable interrupts for this. Our IRQ handlers will crap 
+ * themselves if they take an IRQ in 24bit mode!
  */
 
 static void mac_cache_card_flush(int writeback)
@@ -414,6 +228,8 @@ void __init config_mac(void)
     enable_irq           = mac_enable_irq;
     disable_irq          = mac_disable_irq;
     mach_get_model      = mac_get_model;
+    mach_default_handler = &mac_handlers;
+    mach_get_irq_list    = mac_get_irq_list;
     mach_gettimeoffset   = mac_gettimeoffset;
     mach_gettod          = mac_gettod;
     mach_hwclk           = mac_hwclk;
@@ -489,7 +305,6 @@ struct mac_model *macintosh_config;
 static struct mac_model mac_data_table[]=
 {
        /*
-        *      The default machine, in case we get an unsupported one
         *      We'll pretend to be a Macintosh II, that's pretty safe.
         */
 
@@ -509,10 +324,6 @@ static struct mac_model mac_data_table[]=
         *      Weirdified MacII hardware - all subtley different. Gee thanks
         *      Apple. All these boxes seem to have VIA2 in a different place to
         *      the MacII (+1A000 rather than +4000)
-        *
-        *      The IIfx apparently has different ADB hardware, and stuff
-        *      so zany nobody knows how to drive it.
-        *      Even so, with Marten's help we'll try to deal with it :-)
         * CSA: see http://developer.apple.com/technotes/hw/hw_09.html
         */
 
@@ -584,28 +395,33 @@ static struct mac_model mac_data_table[]=
        {       MAC_MODEL_C660, "Centris 660AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_QUADRA,  MAC_ETHER_MACE,         MAC_NUBUS},
 
        /*
-        *      Power books - seem similar to early Quadras ? (most have 030 though)
+        * The PowerBooks all the same "Combo" custom IC for SCSI and SCC
+        * and a PMU (in two variations?) for ADB. Most of them use the
+        * Quadra-style VIAs. A few models also have IDE from hell.
         */
 
-       {       MAC_MODEL_PB140,  "PowerBook 140",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_NONE,  MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
-       {       MAC_MODEL_PB145,  "PowerBook 145",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE,  MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
-       /*      The PB150 has IDE, and IIci style VIA */
-       {       MAC_MODEL_PB150,  "PowerBook 150",   MAC_ADB_PB1, MAC_VIA_IIci,   MAC_SCSI_NONE, MAC_IDE_PB,    MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
-       {       MAC_MODEL_PB160,  "PowerBook 160",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE,  MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
-       {       MAC_MODEL_PB165,  "PowerBook 165",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE,  MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
-       {       MAC_MODEL_PB165C, "PowerBook 165c",  MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE,  MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
-       {       MAC_MODEL_PB170,  "PowerBook 170",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_NONE,  MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
-       {       MAC_MODEL_PB180,  "PowerBook 180",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE,  MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
-       {       MAC_MODEL_PB180C, "PowerBook 180c",  MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE,  MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
-       {       MAC_MODEL_PB190,  "PowerBook 190cs", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_PB,    MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
-       /* These have onboard SONIC */
-       {       MAC_MODEL_PB520,  "PowerBook 520",   MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE,  MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS},
+       {       MAC_MODEL_PB140,  "PowerBook 140",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_NONE,   MAC_SCC_QUADRA, MAC_ETHER_NONE,        MAC_NUBUS},
+       {       MAC_MODEL_PB145,  "PowerBook 145",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_NONE,   MAC_SCC_QUADRA, MAC_ETHER_NONE,        MAC_NUBUS},
+       {       MAC_MODEL_PB150,  "PowerBook 150",   MAC_ADB_PB1, MAC_VIA_IIci,   MAC_SCSI_OLD,  MAC_IDE_PB,     MAC_SCC_QUADRA, MAC_ETHER_NONE,        MAC_NUBUS},
+       {       MAC_MODEL_PB160,  "PowerBook 160",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_NONE,   MAC_SCC_QUADRA, MAC_ETHER_NONE,        MAC_NUBUS},
+       {       MAC_MODEL_PB165,  "PowerBook 165",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_NONE,   MAC_SCC_QUADRA, MAC_ETHER_NONE,        MAC_NUBUS},
+       {       MAC_MODEL_PB165C, "PowerBook 165c",  MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_NONE,   MAC_SCC_QUADRA, MAC_ETHER_NONE,        MAC_NUBUS},
+       {       MAC_MODEL_PB170,  "PowerBook 170",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_NONE,   MAC_SCC_QUADRA, MAC_ETHER_NONE,        MAC_NUBUS},
+       {       MAC_MODEL_PB180,  "PowerBook 180",   MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_NONE,   MAC_SCC_QUADRA, MAC_ETHER_NONE,        MAC_NUBUS},
+       {       MAC_MODEL_PB180C, "PowerBook 180c",  MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_NONE,   MAC_SCC_QUADRA, MAC_ETHER_NONE,        MAC_NUBUS},
+       {       MAC_MODEL_PB190,  "PowerBook 190",   MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_BABOON, MAC_SCC_QUADRA, MAC_ETHER_NONE,        MAC_NUBUS},
+       {       MAC_MODEL_PB520,  "PowerBook 520",   MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_OLD,  MAC_IDE_NONE,   MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS},
 
        /*
-        *      Power book Duos - similar to Power books, I hope
+        * PowerBook Duos are pretty much like normal PowerBooks
+        * All of these probably have onboard SONIC in the Dock which
+        * means we'll have to probe for it eventually.
+        *
+        * Are these reallly MAC_VIA_IIci? The developer notes for the
+        * Duos show pretty much the same custom parts as in most of
+        * the other PowerBooks which would imply MAC_VIA_QUADRA.
         */
 
-       /* All of these might have onboard SONIC in the Dock but I'm not quite sure */
        {       MAC_MODEL_PB210,  "PowerBook Duo 210",  MAC_ADB_PB2,  MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
        {       MAC_MODEL_PB230,  "PowerBook Duo 230",  MAC_ADB_PB2,  MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
        {       MAC_MODEL_PB250,  "PowerBook Duo 250",  MAC_ADB_PB2,  MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS},
@@ -694,6 +510,7 @@ void mac_identify(void)
        via_init();
        oss_init();
        psc_init();
+       baboon_init();
 }
 
 void mac_report_hardware(void)
@@ -706,137 +523,3 @@ static void mac_get_model(char *str)
        strcpy(str,"Macintosh ");
        strcat(str, macintosh_config->name);
 }
-
-/*
- *     The power switch - yes it's software!
- */
-
-void mac_poweroff(void)
-{
-       /*
-        * MAC_ADB_IISI may need to be moved up here if it doesn't actually
-        * work using the ADB packet method.  --David Kilzer
-        */
-
-       if (oss_present) {
-               oss_poweroff();
-       } else if (macintosh_config->adb_type == MAC_ADB_II) {
-               via_poweroff();
-       } else {
-               adb_poweroff();
-       }
-}
-
-/* 
- * Not all Macs support software power down; for the rest, just 
- * try the ROM reset vector ...
- */
-void mac_reset(void)
-{
-       /*
-        * MAC_ADB_IISI may need to be moved up here if it doesn't actually
-        * work using the ADB packet method.  --David Kilzer
-        */
-
-       if (macintosh_config->adb_type == MAC_ADB_II) {
-               unsigned long cpu_flags;
-
-               /* need ROMBASE in booter */
-               /* indeed, plus need to MAP THE ROM !! */
-
-               if (mac_bi_data.rombase == 0)
-                       mac_bi_data.rombase = 0x40800000;
-
-               /* works on some */
-               rom_reset = (void *) (mac_bi_data.rombase + 0xa);
-
-               if (macintosh_config->ident == MAC_MODEL_SE30) {
-                       /*
-                        * MSch: Machines known to crash on ROM reset ...
-                        */
-                       printk("System halted.\n");
-                       while(1);
-               } else {
-                       save_flags(cpu_flags);
-                       cli();
-
-                       rom_reset();
-
-                       restore_flags(cpu_flags);
-               }
-
-               /* We never make it this far... it usually panics above. */
-               printk ("Restart failed.  Please restart manually.\n");
-
-               /* XXX - delay do we need to spin here ? */
-               while(1);       /* Just in case .. */
-       } else if (macintosh_config->adb_type == MAC_ADB_IISI
-               || macintosh_config->adb_type == MAC_ADB_CUDA) {
-               adb_hwreset();
-       } else if (CPU_IS_030) {
-
-               /* 030-specific reset routine.  The idea is general, but the
-                * specific registers to reset are '030-specific.  Until I
-                * have a non-030 machine, I can't test anything else.
-                *  -- C. Scott Ananian <cananian@alumni.princeton.edu>
-                */
-
-               unsigned long rombase = 0x40000000;
-
-               /* make a 1-to-1 mapping, using the transparent tran. reg. */
-               unsigned long virt = (unsigned long) mac_reset;
-               unsigned long phys = virt_to_phys(mac_reset);
-               unsigned long offset = phys-virt;
-               cli(); /* lets not screw this up, ok? */
-               __asm__ __volatile__(".chip 68030\n\t"
-                                    "pmove %0,%/tt0\n\t"
-                                    ".chip 68k"
-                                    : : "m" ((phys&0xFF000000)|0x8777));
-               /* Now jump to physical address so we can disable MMU */
-               __asm__ __volatile__(
-                    ".chip 68030\n\t"
-                   "lea %/pc@(1f),%/a0\n\t"
-                   "addl %0,%/a0\n\t"/* fixup target address and stack ptr */
-                   "addl %0,%/sp\n\t" 
-                   "pflusha\n\t"
-                   "jmp %/a0@\n\t" /* jump into physical memory */
-                   "0:.long 0\n\t" /* a constant zero. */
-                   /* OK.  Now reset everything and jump to reset vector. */
-                   "1:\n\t"
-                   "lea %/pc@(0b),%/a0\n\t"
-                   "pmove %/a0@, %/tc\n\t" /* disable mmu */
-                   "pmove %/a0@, %/tt0\n\t" /* disable tt0 */
-                   "pmove %/a0@, %/tt1\n\t" /* disable tt1 */
-                   "movel #0, %/a0\n\t"
-                   "movec %/a0, %/vbr\n\t" /* clear vector base register */
-                   "movec %/a0, %/cacr\n\t" /* disable caches */
-                   "movel #0x0808,%/a0\n\t"
-                   "movec %/a0, %/cacr\n\t" /* flush i&d caches */
-                   "movew #0x2700,%/sr\n\t" /* set up status register */
-                   "movel %1@(0x0),%/a0\n\t"/* load interrupt stack pointer */
-                   "movec %/a0, %/isp\n\t" 
-                   "movel %1@(0x4),%/a0\n\t" /* load reset vector */
-                   "reset\n\t" /* reset external devices */
-                   "jmp %/a0@\n\t" /* jump to the reset vector */
-                   ".chip 68k"
-                   : : "r" (offset), "a" (rombase) : "a0");
-
-               /* should never get here */
-               sti(); /* sure, why not */
-               printk ("030 Restart failed.  Please restart manually.\n");
-               while(1);
-       } else {
-               /* We never make it here... The above shoule handle all cases. */
-               printk ("Restart failed.  Please restart manually.\n");
-
-               /* XXX - delay do we need to spin here ? */
-               while(1);       /* Just in case .. */
-       }
-}
-
-/*
- * Local variables:
- *  c-indent-level: 4
- *  tab-width: 8
- * End:
- */
index 21969ce6fd61609a9b61b699e74952a8ca99efb8..3163e3ea35483c971cd549fb9922672fe60c55ee 100644 (file)
@@ -319,7 +319,7 @@ void __init iop_register_interrupts(void)
 {
        if (iop_ism_present) {
                if (oss_present) {
-                       request_irq(OSS_IRQLEV_IOPISM, iop_ism_irq,
+                       sys_request_irq(OSS_IRQLEV_IOPISM, iop_ism_irq,
                                        IRQ_FLG_LOCK, "ISM IOP",
                                        (void *) IOP_NUM_ISM);
                        oss_irq_enable(IRQ_MAC_ADB);
index a795d82ed73a0d2f9d23c94a2f5a96b588180f30..febba19824db9df97c789b2dd15cef372a71393a 100644 (file)
@@ -35,8 +35,8 @@
  *     3       - unused (?)
  *
  *     4       - SCC (slot number determined by reading RR3 on the SSC itself)
- *               - slot 0: SCC channel A
- *               - slot 1: SCC channel B
+ *               - slot 1: SCC channel A
+ *               - slot 2: SCC channel B
  *
  *     5       - unused (?)
  *               [serial errors or special conditions seem to raise level 6
@@ -55,8 +55,8 @@
  *               - slot 5: Slot $E
  *
  *     4       - SCC IOP
- *               - slot 0: SCC channel A
- *               - slot 1: SCC channel B
+ *               - slot 1: SCC channel A
+ *               - slot 2: SCC channel B
  *
  *     5       - ISM IOP (ADB?)
  *
  *   bits.  The handlers for this new machspec interrupt number are then
  *   called. This puts Nubus interrupts into the range 56-62.
  *
+ * - The Baboon interrupts (used on some PowerBooks) are an even more special
+ *   case. They're hidden behind the Nubus slot $C interrupt thus adding a
+ *   third layer of indirection. Why oh why did the Apple engineers do that?
+ *
  * - We support "fast" and "slow" handlers, just like the Amiga port. The
  *   fast handlers are called first and with all interrupts disabled. They
  *   are expected to execute quickly (hence the name). The slow handlers are
  *   called last with interrupts enabled and the interrupt level restored.
  *   They must therefore be reentrant.
  *
- * - Drivers should never try to request autovector interrupt numbers. It
- *   won't work.
- *
  *   TODO:
  *
- *   o Perhaps build some intelligence into mac_SCC_handler(); we could check
- *     the SCC ourselves and only call the handler for the appopriate channel.
  */
 
 #include <linux/types.h>
 #include <asm/system.h>
 #include <asm/irq.h>
 #include <asm/traps.h>
+#include <asm/bootinfo.h>
 #include <asm/machw.h>
 #include <asm/macintosh.h>
 #include <asm/mac_via.h>
 
 #include <asm/macints.h>
 
+/*
+ * The mac_irq_list array is an array of linked lists of irq_node_t nodes.
+ * Each node contains one handler to be called whenever the interrupt
+ * occurs, with fast handlers listed before slow handlers.
+ */
+
+irq_node_t *mac_irq_list[NUM_MAC_SOURCES];
+
+/* SCC interrupt mask */
+
+static int scc_mask;
+
 /*
  * VIA/RBV hooks
  */
@@ -175,6 +187,26 @@ extern int  psc_irq_pending(int);
 
 extern void iop_register_interrupts(void);
 
+/*
+ * Baboon hooks
+ */
+
+extern int baboon_present;
+
+extern void baboon_init(void);
+extern void baboon_register_interrupts(void);
+extern void baboon_irq_enable(int);
+extern void baboon_irq_disable(int);
+extern void baboon_irq_clear(int);
+extern int  baboon_irq_pending(int);
+
+/*
+ * SCC interrupt routines
+ */
+
+static void scc_irq_enable(int);
+static void scc_irq_disable(int);
+
 /*
  * console_loglevel determines NMI handler function
  */
@@ -184,17 +216,27 @@ extern int console_loglevel;
 extern void mac_bang(int, void *, struct pt_regs *);
 
 void mac_nmi_handler(int, void *, struct pt_regs *);
-void mac_SCC_handler(int, void *, struct pt_regs *);
+void mac_debug_handler(int, void *, struct pt_regs *);
 
 /* #define DEBUG_MACINTS */
 
 void mac_init_IRQ(void)
 {
+        int i;
+
 #ifdef DEBUG_MACINTS
        printk("mac_init_IRQ(): Setting things up...\n");
 #endif
+       /* Initialize the IRQ handler lists. Initially each list is empty, */
+
+       for (i = 0; i < NUM_MAC_SOURCES; i++) {
+               mac_irq_list[i] = NULL;
+       }
+
+       scc_mask = 0;
+
        /* 
-        * Register the handlers for the the master IRQ handlers
+        * Now register the handlers for the the master IRQ handlers
         * at levels 1-7. Most of the work is done elsewhere.
         */
 
@@ -204,8 +246,9 @@ void mac_init_IRQ(void)
                via_register_interrupts();
        }
        if (psc_present) psc_register_interrupts();
+       if (baboon_present) baboon_register_interrupts();
        iop_register_interrupts();
-       request_irq(7, mac_nmi_handler, IRQ_FLG_LOCK, "NMI", mac_nmi_handler);
+       sys_request_irq(7, mac_nmi_handler, IRQ_FLG_LOCK, "NMI", mac_nmi_handler);
 #ifdef DEBUG_MACINTS
        printk("mac_init_IRQ(): Done!\n");
 #endif
@@ -284,27 +327,20 @@ static inline void mac_delete_irq(irq_node_t **list, void *dev_id)
 
 void mac_do_irq_list(int irq, struct pt_regs *fp)
 {
-       irq_node_t *node, *slow_nodes, **list = NULL;
+       irq_node_t *node, *slow_nodes;
        unsigned long cpu_flags;
 
        kstat.irqs[0][irq]++;
 
-       if (irq < VIA1_SOURCE_BASE) {
-               list = &autoirq_list[irq];
-       } else if (irq < NUM_MAC_SOURCES) {
-               list = &userirq_list[irq - VIA1_SOURCE_BASE];
-       }
-       if (!list) return;
-
 #ifdef DEBUG_SPURIOUS
-       if (!*list && (console_loglevel > 7)) {
+       if (!mac_irq_list[irq] && (console_loglevel > 7)) {
                printk("mac_do_irq_list: spurious interrupt %d!\n", irq);
                return;
        }
 #endif
 
        /* serve first fast and normal handlers */
-       for (node = *list;
+       for (node = mac_irq_list[irq];
             node && (!(node->flags & IRQ_FLG_SLOW));
             node = node->next)
                node->handler(irq, node->dev_id, fp);
@@ -329,7 +365,9 @@ void mac_do_irq_list(int irq, struct pt_regs *fp)
 
 void mac_enable_irq (unsigned int irq)
 {
-       switch(IRQ_SRC(irq)) {
+       int irq_src     = IRQ_SRC(irq);
+
+       switch(irq_src) {
                case 1: via_irq_enable(irq);
                        break;
                case 2:
@@ -346,6 +384,12 @@ void mac_enable_irq (unsigned int irq)
                                psc_irq_enable(irq);
                        } else if (oss_present) {
                                oss_irq_enable(irq);
+                       } else if (irq_src == 4) {
+                               scc_irq_enable(irq);
+                       }
+                       break;
+               case 8: if (baboon_present) {
+                               baboon_irq_enable(irq);
                        }
                        break;
        }
@@ -353,12 +397,13 @@ void mac_enable_irq (unsigned int irq)
 
 void mac_disable_irq (unsigned int irq)
 {
-       switch(IRQ_SRC(irq)) {
+       int irq_src     = IRQ_SRC(irq);
+
+       switch(irq_src) {
                case 1: via_irq_disable(irq);
                        break;
                case 2:
-               case 7:
-                       if (oss_present) {
+               case 7: if (oss_present) {
                                oss_irq_disable(irq);
                        } else {
                                via_irq_disable(irq);
@@ -371,6 +416,12 @@ void mac_disable_irq (unsigned int irq)
                                psc_irq_disable(irq);
                        } else if (oss_present) {
                                oss_irq_disable(irq);
+                       } else if (irq_src == 4) {
+                               scc_irq_disable(irq);
+                       }
+                       break;
+               case 8: if (baboon_present) {
+                               baboon_irq_disable(irq);
                        }
                        break;
        }
@@ -397,6 +448,10 @@ void mac_clear_irq( unsigned int irq )
                                oss_irq_clear(irq);
                        }
                        break;
+               case 8: if (baboon_present) {
+                               baboon_irq_clear(irq);
+                       }
+                       break;
        }
 }
 
@@ -434,29 +489,37 @@ int mac_request_irq(unsigned int irq,
                    void (*handler)(int, void *, struct pt_regs *),
                    unsigned long flags, const char *devname, void *dev_id)
 {
-       int vec, ret;
+       irq_node_t *node;
 
 #ifdef DEBUG_MACINTS
        printk ("%s: irq %d requested for %s\n", __FUNCTION__, irq, devname);
 #endif
 
        if (irq < VIA1_SOURCE_BASE) {
-               vec = VEC_SPUR + irq;
-       } else if (irq < NUM_MAC_SOURCES) {
-               vec = VEC_USER + irq - VIA1_SOURCE_BASE;
-       } else {
+               return sys_request_irq(irq, handler, flags, devname, dev_id);
+       }
+
+       if (irq >= NUM_MAC_SOURCES) {
                printk ("%s: unknown irq %d requested by %s\n",
                        __FUNCTION__, irq, devname);
-               return -EAGAIN;
        }
 
-       ret = sys_request_listirq(vec, handler, flags, devname, dev_id);
-       if (!ret) {
-               vectors[vec] = autoirq_listhandler;
-               mac_enable_irq(irq);
-       }
+       /* Get a node and stick it onto the right list */
+
+       if (!(node = new_irq_node())) return -ENOMEM;
+
+       node->handler   = handler;
+       node->flags     = flags;
+       node->dev_id    = dev_id;
+       node->devname   = devname;
+       node->next      = NULL;
+       mac_insert_irq(&mac_irq_list[irq], node);
+
+       /* Now enable the IRQ source */
+
+       mac_enable_irq(irq);
 
-       return ret;
+       return 0;
 }
                             
 /*
@@ -465,30 +528,120 @@ int mac_request_irq(unsigned int irq,
 
 void mac_free_irq(unsigned int irq, void *dev_id)
 {
-       irq_node_t **list = NULL;
-       int vec = 0;
-
 #ifdef DEBUG_MACINTS
        printk ("%s: irq %d freed by %p\n", __FUNCTION__, irq, dev_id);
 #endif
 
        if (irq < VIA1_SOURCE_BASE) {
-               vec = VEC_SPUR + irq;
-               list = &autoirq_list[irq];
-       } else if (irq < NUM_MAC_SOURCES) {
-               vec = VEC_USER + irq - VIA1_SOURCE_BASE;
-               list = &userirq_list[irq - VIA1_SOURCE_BASE];
+               return sys_free_irq(irq, dev_id);
+       }
+
+       if (irq >= NUM_MAC_SOURCES) {
+               printk ("%s: unknown irq %d freed\n",
+                       __FUNCTION__, irq);
+               return;
        }
-       if (!list) return;
 
-       sys_free_irq(vec, dev_id);
+       mac_delete_irq(&mac_irq_list[irq], dev_id);
 
        /* If the list for this interrupt is */
        /* empty then disable the source.    */
 
-       if (!*list) {
+       if (!mac_irq_list[irq]) {
                mac_disable_irq(irq);
-               vectors[vec] = bad_interrupt;
+       }
+}
+
+/*
+ * Generate a pretty listing for /proc/interrupts
+ *
+ * By the time we're called the autovector interrupt list has already been
+ * generated, so we just need to do the machspec interrupts.
+ *
+ * 990506 (jmt) - rewritten to handle chained machspec interrupt handlers.
+ *                Also removed display of num_spurious it is already
+ *               displayed for us as autovector irq 0.
+ */
+
+int mac_get_irq_list (char *buf)
+{
+       int i, len = 0;
+       irq_node_t *node;
+       char *base;
+
+       /* Don't do Nubus interrupts in this loop; we do them separately  */
+       /* below so that we can print slot numbers instead of IRQ numbers */
+
+       for (i = VIA1_SOURCE_BASE ; i < NUM_MAC_SOURCES ; ++i) {
+
+               /* Nonexistant interrupt or nothing registered; skip it. */
+
+               if ((node = mac_irq_list[i]) == NULL) continue;
+               if (node->flags & IRQ_FLG_STD) continue;
+
+               base = "";
+               switch(IRQ_SRC(i)) {
+                       case 1: base = "via1";
+                               break;
+                       case 2: if (oss_present) {
+                                       base = "oss";
+                               } else {
+                                       base = "via2";
+                               }
+                               break;
+                       case 3:
+                       case 4:
+                       case 5:
+                       case 6: if (psc_present) {
+                                       base = "psc";
+                               } else if (oss_present) {
+                                       base = "oss";
+                               } else {
+                                       if (IRQ_SRC(i) == 4) base = "scc";
+                               }
+                               break;
+                       case 7: base = "nbus";
+                               break;
+                       case 8: base = "bbn";
+                               break;
+               }
+               len += sprintf(buf+len, "%4s %2d: %10u ",
+                               base, i, kstat.irqs[0][i]);
+
+               do {
+                       if (node->flags & IRQ_FLG_FAST) {
+                               len += sprintf(buf+len, "F ");
+                       } else if (node->flags & IRQ_FLG_SLOW) {
+                               len += sprintf(buf+len, "S ");
+                       } else {
+                               len += sprintf(buf+len, "  ");
+                       }
+                       len += sprintf(buf+len, "%s\n", node->devname);
+                       if ((node = node->next)) {
+                               len += sprintf(buf+len, "                    ");
+                       }
+               } while(node);
+
+       }
+       return len;
+}
+
+void mac_default_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+#ifdef DEBUG_SPURIOUS
+       if (console_loglevel > 6) {
+               printk("Unexpected IRQ %d on device %p\n", irq, dev_id);
+       }
+#endif
+}
+
+static int num_debug[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+void mac_debug_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       if (num_debug[irq] < 10) {
+               printk("DEBUG: Unexpected IRQ %d\n", irq);
+               num_debug[irq]++;
        }
 }
 
@@ -543,12 +696,53 @@ void mac_nmi_handler(int irq, void *dev_id, struct pt_regs *fp)
 }
 
 /*
- *     SCC master interrupt handler; sole purpose: pass the registered 
- *     async struct to the SCC handler proper.
+ * Simple routines for masking and unmasking
+ * SCC interrupts in cases where this can't be
+ * done in hardware (only the PSC can do that.)
+ */
+
+static void scc_irq_enable(int irq) {
+       int irq_idx     = IRQ_IDX(irq);
+
+       scc_mask |= (1 << irq_idx);
+}
+
+static void scc_irq_disable(int irq) {
+       int irq_idx     = IRQ_IDX(irq);
+
+       scc_mask &= ~(1 << irq_idx);
+}
+
+/*
+ * SCC master interrupt handler. We have to do a bit of magic here
+ * to figure out what channel gave us the interrupt; putting this
+ * here is cleaner than hacking it into drivers/char/macserial.c.
  */
 
-void mac_SCC_handler(int irq, void *dev_id, struct pt_regs *regs)
+void mac_scc_dispatch(int irq, void *dev_id, struct pt_regs *regs)
 {
-       mac_do_irq_list(IRQ_SCCA, regs);
-       mac_do_irq_list(IRQ_SCCB, regs);
+       volatile unsigned char *scc = (unsigned char *) mac_bi_data.sccbase + 2;
+       unsigned char reg;
+       unsigned long cpu_flags;
+
+       /* Read RR3 from the chip. Always do this on channel A */
+       /* This must be an atomic operation so disable irqs.   */
+
+       save_flags(cpu_flags); cli();
+       *scc = 3;
+       reg = *scc;
+       restore_flags(cpu_flags);
+
+       /* Now dispatch. Bits 0-2 are for channel B and */
+       /* bits 3-5 are for channel A. We can safely    */
+       /* ignore the remaining bits here.              */
+       /*                                              */
+       /* Note that we're ignoring scc_mask for now.   */
+       /* If we actually mask the ints then we tend to */
+       /* get hammered by very persistant SCC irqs,    */
+       /* and since they're autovector interrupts they */
+       /* pretty much kill the system.                 */
+
+       if (reg & 0x38) mac_do_irq_list(IRQ_SCCA, regs);
+       if (reg & 0x07) mac_do_irq_list(IRQ_SCCB, regs);
 }
diff --git a/arch/m68k/mac/mackeyb.c b/arch/m68k/mac/mackeyb.c
deleted file mode 100644 (file)
index 0d70357..0000000
+++ /dev/null
@@ -1,762 +0,0 @@
-/*
- * linux/arch/m68k/mac/mackeyb.c
- *
- * Keyboard driver for Macintosh computers.
- *
- * Adapted from drivers/macintosh/key_mac.c and arch/m68k/atari/akakeyb.c 
- * (see that file for its authors and contributors).
- *
- * Copyright (C) 1997 Michael Schmitz.
- *
- * 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.
- */
-
-/*
- * misc. keyboard stuff (everything not in adb-bus.c or keyb_m68k.c)
- */
-
-#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/mm.h>
-#include <linux/kd.h>
-#include <linux/tty.h>
-#include <linux/console.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-/* keyb */
-#include <linux/keyboard.h>
-#include <linux/random.h>
-#include <linux/delay.h>
-/* keyb */
-
-#include <asm/setup.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/pgtable.h>
-#include <asm/machdep.h>
-
-#include <asm/macintosh.h>
-#include <asm/macints.h>
-/* for keyboard_input stuff */
-#include <asm/adb.h>
-#define KEYB_KEYREG    0       /* register # for key up/down data */
-#define KEYB_LEDREG    2       /* register # for leds on ADB keyboard */
-#define MOUSE_DATAREG  0       /* reg# for movement/button codes from mouse */
-/* end keyboard_input stuff */
-
-#include <linux/kbd_kern.h>
-#include <linux/kbd_ll.h>
-
-static void kbd_repeat(unsigned long);
-static struct timer_list repeat_timer = { NULL, NULL, 0, 0, kbd_repeat };
-static int last_keycode;
-
-static void input_keycode(int, int);
-
-extern struct kbd_struct kbd_table[];
-
-extern void adb_bus_init(void);
-extern void handle_scancode(unsigned char, int);
-extern void put_queue(int);
-
-/* keyb */
-static void mac_leds_done(struct adb_request *);
-static void keyboard_input(unsigned char *, int, struct pt_regs *);
-static void mouse_input(unsigned char *, int, struct pt_regs *);
-
-#ifdef CONFIG_ADBMOUSE
-/* XXX: Hook for mouse driver */
-void (*adb_mouse_interrupt_hook)(unsigned char *, int);
-int adb_emulate_buttons = 0;
-int adb_button2_keycode = 0x7d;        /* right control key */
-int adb_button3_keycode = 0x7c; /* right option key */
-#endif
-
-/* The mouse driver - for debugging */
-extern void adb_mouse_interrupt(char *, int);
-/* end keyb */
-
-/* this map indicates which keys shouldn't autorepeat. */
-static unsigned char dont_repeat[128] = {
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* esc...option */
-       0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* num lock */
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, /* scroll lock */
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-};
-
-/*
- * Mac private key maps
- */
-u_short mac_plain_map[NR_KEYS] __initdata = {
-       0xfb61, 0xfb73, 0xfb64, 0xfb66, 0xfb68, 0xfb67, 0xfb7a, 0xfb78,
-       0xfb63, 0xfb76, 0xf200, 0xfb62, 0xfb71, 0xfb77, 0xfb65, 0xfb72,
-       0xfb79, 0xfb74, 0xf031, 0xf032, 0xf033, 0xf034, 0xf036, 0xf035,
-       0xf03d, 0xf039, 0xf037, 0xf02d, 0xf038, 0xf030, 0xf05d, 0xfb6f,
-       0xfb75, 0xf05b, 0xfb69, 0xfb70, 0xf201, 0xfb6c, 0xfb6a, 0xf027,
-       0xfb6b, 0xf03b, 0xf05c, 0xf02c, 0xf02f, 0xfb6e, 0xfb6d, 0xf02e,
-       0xf009, 0xf020, 0xf060, 0xf07f, 0xf200, 0xf01b, 0xf702, 0xf703,
-       0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
-       0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
-       0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
-       0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
-       0xf306, 0xf307, 0xfb61, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
-       0xf104, 0xf105, 0xf106, 0xf102, 0xf107, 0xf108, 0xf200, 0xf10a,
-       0xf200, 0xf10c, 0xf200, 0xf209, 0xf200, 0xf109, 0xf200, 0xf10b,
-       0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf103, 0xf117,
-       0xf101, 0xf119, 0xf100, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short mac_shift_map[NR_KEYS] __initdata = {
-       0xfb41, 0xfb53, 0xfb44, 0xfb46, 0xfb48, 0xfb47, 0xfb5a, 0xfb58,
-       0xfb43, 0xfb56, 0xf200, 0xfb42, 0xfb51, 0xfb57, 0xfb45, 0xfb52,
-       0xfb59, 0xfb54, 0xf021, 0xf040, 0xf023, 0xf024, 0xf05e, 0xf025,
-       0xf02b, 0xf028, 0xf026, 0xf05f, 0xf02a, 0xf029, 0xf07d, 0xfb4f,
-       0xfb55, 0xf07b, 0xfb49, 0xfb50, 0xf201, 0xfb4c, 0xfb4a, 0xf022,
-       0xfb4b, 0xf03a, 0xf07c, 0xf03c, 0xf03f, 0xfb4e, 0xfb4d, 0xf03e,
-       0xf009, 0xf020, 0xf07e, 0xf07f, 0xf200, 0xf01b, 0xf702, 0xf703,
-       0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
-       0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
-       0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
-       0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
-       0xf306, 0xf307, 0xfb41, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
-       0xf10e, 0xf10f, 0xf110, 0xf10c, 0xf111, 0xf112, 0xf200, 0xf10a,
-       0xf200, 0xf10c, 0xf200, 0xf203, 0xf200, 0xf113, 0xf200, 0xf10b,
-       0xf200, 0xf11d, 0xf115, 0xf114, 0xf20b, 0xf116, 0xf10d, 0xf117,
-       0xf10b, 0xf20a, 0xf10a, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short mac_altgr_map[NR_KEYS] __initdata = {
-       0xf914, 0xfb73, 0xf917, 0xf919, 0xfb68, 0xfb67, 0xfb7a, 0xfb78,
-       0xf916, 0xfb76, 0xf200, 0xf915, 0xfb71, 0xfb77, 0xf918, 0xfb72,
-       0xfb79, 0xfb74, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200,
-       0xf200, 0xf05d, 0xf07b, 0xf05c, 0xf05b, 0xf07d, 0xf07e, 0xfb6f,
-       0xfb75, 0xf200, 0xfb69, 0xfb70, 0xf201, 0xfb6c, 0xfb6a, 0xf200,
-       0xfb6b, 0xf200, 0xf200, 0xf200, 0xf200, 0xfb6e, 0xfb6d, 0xf200,
-       0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf703,
-       0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
-       0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
-       0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
-       0xf200, 0xf200, 0xf90a, 0xf90b, 0xf90c, 0xf90d, 0xf90e, 0xf90f,
-       0xf910, 0xf911, 0xf914, 0xf912, 0xf913, 0xf200, 0xf200, 0xf200,
-       0xf510, 0xf511, 0xf512, 0xf50e, 0xf513, 0xf514, 0xf200, 0xf516,
-       0xf200, 0xf10c, 0xf200, 0xf202, 0xf200, 0xf515, 0xf200, 0xf517,
-       0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf50f, 0xf117,
-       0xf50d, 0xf119, 0xf50c, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short mac_ctrl_map[NR_KEYS] __initdata = {
-       0xf001, 0xf013, 0xf004, 0xf006, 0xf008, 0xf007, 0xf01a, 0xf018,
-       0xf003, 0xf016, 0xf200, 0xf002, 0xf011, 0xf017, 0xf005, 0xf012,
-       0xf019, 0xf014, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01e, 0xf01d,
-       0xf200, 0xf200, 0xf01f, 0xf01f, 0xf07f, 0xf200, 0xf01d, 0xf00f,
-       0xf015, 0xf01b, 0xf009, 0xf010, 0xf201, 0xf00c, 0xf00a, 0xf007,
-       0xf00b, 0xf200, 0xf01c, 0xf200, 0xf07f, 0xf00e, 0xf00d, 0xf20e,
-       0xf200, 0xf000, 0xf000, 0xf008, 0xf200, 0xf200, 0xf702, 0xf703,
-       0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
-       0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
-       0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
-       0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
-       0xf306, 0xf307, 0xf001, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
-       0xf104, 0xf105, 0xf106, 0xf102, 0xf107, 0xf108, 0xf200, 0xf10a,
-       0xf200, 0xf10c, 0xf200, 0xf204, 0xf200, 0xf109, 0xf200, 0xf10b,
-       0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf103, 0xf117,
-       0xf101, 0xf119, 0xf100, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short mac_shift_ctrl_map[NR_KEYS] __initdata = {
-       0xf001, 0xf013, 0xf004, 0xf006, 0xf008, 0xf007, 0xf01a, 0xf018,
-       0xf003, 0xf016, 0xf200, 0xf002, 0xf011, 0xf017, 0xf005, 0xf012,
-       0xf019, 0xf014, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200,
-       0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, 0xf00f,
-       0xf015, 0xf200, 0xf009, 0xf010, 0xf201, 0xf00c, 0xf00a, 0xf200,
-       0xf00b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf00e, 0xf00d, 0xf200,
-       0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf703,
-       0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
-       0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
-       0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
-       0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
-       0xf306, 0xf307, 0xf001, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
-       0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-       0xf200, 0xf10c, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-       0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf200, 0xf117,
-       0xf200, 0xf119, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf20c,
-};
-
-u_short mac_alt_map[NR_KEYS] __initdata = {
-       0xf861, 0xf873, 0xf864, 0xf866, 0xf868, 0xf867, 0xf87a, 0xf878,
-       0xf863, 0xf876, 0xf200, 0xf862, 0xf871, 0xf877, 0xf865, 0xf872,
-       0xf879, 0xf874, 0xf831, 0xf832, 0xf833, 0xf834, 0xf836, 0xf835,
-       0xf83d, 0xf839, 0xf837, 0xf82d, 0xf838, 0xf830, 0xf85d, 0xf86f,
-       0xf875, 0xf85b, 0xf869, 0xf870, 0xf80d, 0xf86c, 0xf86a, 0xf827,
-       0xf86b, 0xf83b, 0xf85c, 0xf82c, 0xf82f, 0xf86e, 0xf86d, 0xf82e,
-       0xf809, 0xf820, 0xf860, 0xf87f, 0xf200, 0xf81b, 0xf702, 0xf703,
-       0xf700, 0xf207, 0xf701, 0xf210, 0xf211, 0xf600, 0xf603, 0xf200,
-       0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
-       0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
-       0xf200, 0xf200, 0xf900, 0xf901, 0xf902, 0xf903, 0xf904, 0xf905,
-       0xf906, 0xf907, 0xf861, 0xf908, 0xf909, 0xf200, 0xf200, 0xf200,
-       0xf504, 0xf505, 0xf506, 0xf502, 0xf507, 0xf508, 0xf200, 0xf50a,
-       0xf200, 0xf10c, 0xf200, 0xf209, 0xf200, 0xf509, 0xf200, 0xf50b,
-       0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf503, 0xf117,
-       0xf501, 0xf119, 0xf500, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-u_short mac_ctrl_alt_map[NR_KEYS] __initdata = {
-       0xf801, 0xf813, 0xf804, 0xf806, 0xf808, 0xf807, 0xf81a, 0xf818,
-       0xf803, 0xf816, 0xf200, 0xf802, 0xf811, 0xf817, 0xf805, 0xf812,
-       0xf819, 0xf814, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-       0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf80f,
-       0xf815, 0xf200, 0xf809, 0xf810, 0xf201, 0xf80c, 0xf80a, 0xf200,
-       0xf80b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf80e, 0xf80d, 0xf200,
-       0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf703,
-       0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
-       0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
-       0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
-       0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
-       0xf306, 0xf307, 0xf801, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
-       0xf504, 0xf505, 0xf506, 0xf502, 0xf507, 0xf508, 0xf200, 0xf50a,
-       0xf200, 0xf10c, 0xf200, 0xf200, 0xf200, 0xf509, 0xf200, 0xf50b,
-       0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf503, 0xf117,
-       0xf501, 0xf119, 0xf500, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-};
-
-extern unsigned int keymap_count;
-
-/*
- * Misc. defines for testing 
- */
-
-extern int console_loglevel;
-
-static struct adb_request led_request;
-extern int in_keybinit;
-
-/*
- * machdep keyboard routines, interface and key repeat method modeled after
- * drivers/macintosh/keyb_mac.c
- */
-
-int mac_kbd_translate(unsigned char keycode, unsigned char *keycodep,
-                    char raw_mode)
-{
-       if (!raw_mode) {
-               /*
-                * Convert R-shift/control/option to L version.
-                * Remap keycode 0 (A) to the unused keycode 0x5a.
-                * Other parts of the system assume 0 is not a valid keycode.
-                */
-               switch (keycode) {
-               case 0x7b: keycode = 0x38; break; /* R-shift */
-               case 0x7c: keycode = 0x3a; break; /* R-option */
-               case 0x7d: keycode = 0x36; break; /* R-control */
-               case 0:    keycode = 0x5a; break; /* A */
-               }
-       }
-       *keycodep = keycode;
-       return 1;
-}
-
-int mac_kbd_unexpected_up(unsigned char keycode)
-{
-       return 0x80;
-}
-
-static void
-keyboard_input(unsigned char *data, int nb, struct pt_regs *regs)
-{
-       /* first check this is from register 0 */
-       if (nb != 5 || (data[2] & 3) != KEYB_KEYREG)
-               return;         /* ignore it */
-       kbd_pt_regs = regs;
-       input_keycode(data[3], 0);
-       if (!(data[4] == 0xff || (data[4] == 0x7f && data[3] == 0x7f)))
-               input_keycode(data[4], 0);
-}
-
-static void
-input_keycode(int keycode, int repeat)
-{
-       struct kbd_struct *kbd;
-       int up_flag;
-
-       kbd = kbd_table + fg_console;
-       up_flag = (keycode & 0x80);
-        keycode &= 0x7f;
-
-       if (!repeat)
-               del_timer(&repeat_timer);
-
-#ifdef CONFIG_ADBMOUSE
-       /*
-        * XXX: Add mouse button 2+3 fake codes here if mouse open.
-        *      As we only report up/down events, keep track of faked buttons.
-        *      Really messy; might need to check if keyboard is in
-        *      VC_RAW mode for X?.
-        *      Might also want to know how many buttons need to be emulated.
-        *      -> hide this as function in arch/m68k/mac ?
-        *      Current emulation buttons: right alt/option and control
-        *      (wanted: command and alt/option, or KP= and KP( ...)
-        *      Debug version; might be rewritten to be faster on normal keys.
-        */
-       if (adb_emulate_buttons 
-           && (adb_mouse_interrupt_hook || console_loglevel >= 8)) {
-               unsigned char button, button2, button3, fake_event;
-               static unsigned char button2state=0, button3state=0; /* up */
-               /* faked ADB packet */
-               static unsigned char data[4] = { 0, 0x80, 0x80, 0x80 };
-
-               button = 0;
-               fake_event = 0;
-               if (keycode == adb_button2_keycode) {   /* which 'button' ? */
-                       /* R-option */
-                       button2 = (!up_flag);           /* new state */
-                       if (button2 != button2state)    /* change ? */
-                               button = 2; 
-                       button2state = button2;         /* save state */
-                       fake_event = 2;
-               } else if (keycode == adb_button3_keycode) {
-                       /* R-control */
-                       button3 = (!up_flag);           /* new state */
-                       if (button3 != button3state)    /* change ? */ 
-                               button = 3; 
-                       button3state = button3;         /* save state */
-                       fake_event = 3;
-               }
-#ifdef DEBUG_ADBMOUSE
-               if (fake_event && console_loglevel >= 8)
-                       printk("fake event: button2 %d button3 %d button %d\n",
-                                button2state, button3state, button);
-#endif
-               if (button) {           /* there's been a button state change */
-                       /* fake a mouse packet : send all bytes, change one! */
-                       data[button] = (up_flag ? 0x80 : 0);
-                       if (adb_mouse_interrupt_hook)
-                               adb_mouse_interrupt_hook(data, 4);
-#ifdef DEBUG_ADBMOUSE
-                       else
-                               printk("mouse_fake: data %2x %2x %2x buttons %2x \n", 
-                                       data[1], data[2], data[3],
-                                       ~( (data[1] & 0x80 ? 0 : 4) 
-                                        | (data[2] & 0x80 ? 0 : 1) 
-                                        | (data[3] & 0x80 ? 0 : 2) )&7 );
-#endif
-               }
-               /*
-                * for mouse 3-button emulation: don't process 'fake' keys!
-                * Keys might autorepeat, and console state gets generally messed
-                * up enough so that selection stops working.
-                */
-               if (fake_event)
-                       return;
-       }
-#endif /* CONFIG_ADBMOUSE */
-
-       /*
-        * Convert R-shift/control/option to L version.
-        */
-       switch (keycode) {
-               case 0x7b: keycode = 0x38; break; /* R-shift */
-               case 0x7c: keycode = 0x3a; break; /* R-option */
-               case 0x7d: keycode = 0x36; break; /* R-control */
-               case 0x0:  if (kbd->kbdmode != VC_RAW) 
-                              keycode = 0x5a; /* A; keycode 0 deprecated */
-                              break; 
-       }
-
-       if (kbd->kbdmode != VC_RAW) {
-               if (!up_flag && !dont_repeat[keycode]) {
-                       last_keycode = keycode;
-                       repeat_timer.expires = jiffies + (repeat? HZ/15: HZ/2);
-                       add_timer(&repeat_timer);
-               }
-
-               /*
-                * XXX fix caps-lock behaviour by turning the key-up
-                * transition into a key-down transition.
-                * MSch: need to turn each caps-lock event into a down-up
-                * double event (keyboard code assumes caps-lock is a toggle)
-                * 981127: fix LED behavior (kudos atong!)
-                */
-               switch (keycode) {
-               case 0x39:
-                       handle_scancode(keycode, 1);    /* down */
-                       up_flag = 0x80;                 /* see below ... */
-                       mark_bh(KEYBOARD_BH);
-                       break;
-                case 0x47:
-                       mark_bh(KEYBOARD_BH);
-                       break;
-               }
-       }
-
-       handle_scancode(keycode, !up_flag);
-}
-
-static void
-kbd_repeat(unsigned long xxx)
-{
-       unsigned long flags;
-
-       save_flags(flags);
-       cli();
-       input_keycode(last_keycode, 1);
-       restore_flags(flags);
-}
-
-  /* [ACA:23-Mar-97] Three button mouse support.  This is designed to
-     function with MkLinux DR-2.1 style X servers.  It only works with
-     three-button mice that conform to Apple's multi-button mouse
-     protocol. */
-
-  /*
-    The X server for MkLinux DR2.1 uses the following unused keycodes to
-    read the mouse:
-
-    0x7e  This indicates that the next two keycodes should be interpreted
-          as mouse information.  The first following byte's high bit
-          represents the state of the left button.  The lower seven bits
-          represent the x-axis acceleration.  The lower seven bits of the
-          second byte represent y-axis acceleration.
-
-    0x3f  The x server interprets this keycode as a middle button
-          release.
-
-    0xbf  The x server interprets this keycode as a middle button
-          depress.
-
-    0x40  The x server interprets this keycode as a right button
-          release.
-
-    0xc0  The x server interprets this keycode as a right button
-          depress.
-
-    NOTES: There should be a better way of handling mice in the X server.
-    The MOUSE_ESCAPE code (0x7e) should be followed by three bytes instead
-    of two.  The three mouse buttons should then, in the X server, be read
-    as the high-bits of all three bytes.  The x and y motions can still be
-    in the first two bytes.  Maybe I'll do this...
-  */
-
-  /*
-    Handler 4 -- Apple Extended mouse protocol.
-
-    For Apple's 3-button mouse protocol the data array will contain the
-    following values:
-
-               BITS    COMMENTS
-    data[0] = 0000 0000 ADB packet identifer.
-    data[1] = 0100 0000 Extended protocol register.
-             Bits 6-7 are the device id, which should be 1.
-             Bits 4-5 are resolution which is in "units/inch".
-             The Logitech MouseMan returns these bits clear but it has
-             200/300cpi resolution.
-             Bits 0-3 are unique vendor id.
-    data[2] = 0011 1100 Bits 0-1 should be zero for a mouse device.
-             Bits 2-3 should be 8 + 4.
-                     Bits 4-7 should be 3 for a mouse device.
-    data[3] = bxxx xxxx Left button and x-axis motion.
-    data[4] = byyy yyyy Second button and y-axis motion.
-    data[5] = byyy bxxx Third button and fourth button.  Y is additional
-             high bits of y-axis motion.  XY is additional
-             high bits of x-axis motion.
-
-    NOTE: data[0] and data[2] are confirmed by the parent function and
-    need not be checked here.
-  */
-
-  /*
-    Handler 1 -- 100cpi original Apple mouse protocol.
-    Handler 2 -- 200cpi original Apple mouse protocol.
-
-    For Apple's standard one-button mouse protocol the data array will
-    contain the following values:
-
-                BITS    COMMENTS
-    data[0] = 0000 0000 ADB packet identifer.
-    data[1] = ???? ???? (?)
-    data[2] = ???? ??00 Bits 0-1 should be zero for a mouse device.
-    data[3] = bxxx xxxx First button and x-axis motion.
-    data[4] = byyy yyyy Second button and y-axis motion.
-
-    NOTE: data[0] is confirmed by the parent function and need not be
-    checked here.
-  */
-
-static void
-mouse_input(unsigned char *data, int nb, struct pt_regs *regs)
-{
-       struct kbd_struct *kbd;
-       int i;
-
-       if (nb < 5 || nb > 6 || (data[2] & 3) != MOUSE_DATAREG) {
-               printk("data from mouse:");
-               for (i = 0; i < nb; ++i)
-                       printk(" %x", data[i]);
-               printk("\n");
-               return;
-       }
-
-       if (adb_mouse_interrupt_hook) {
-               adb_mouse_interrupt_hook(data+2, nb-2);
-               /*
-                * passing the mouse data to i.e. the X server as done for
-                * Xpmac will confuse applications on a sane X server :-)
-                */
-               return;
-       } 
-#ifdef DEBUG_ADBMOUSE
-       else
-               if (console_loglevel >= 8)
-                   printk("mouse_input: data %x %x %x buttons %x dx %d dy %d \n", 
-                       data[3], data[4], data[5],
-                       ~((data[3] & 0x80 ? 0 : 4) 
-                       | (data[4] & 0x80 ? 0 : 1)
-                       | (data[5] & 0x80 ? 0 : 2))&7,
-                       ((data[4]&0x7f) < 64 ? (data[4]&0x7f) : (data[4]&0x7f)-128 ),
-                       ((data[3]&0x7f) < 64 ? -(data[3]&0x7f) : 128-(data[3]&0x7f) ) );
-#endif
-
-
-       kbd = kbd_table + fg_console;
-
-#if 0  /* The entirely insane way of MkLinux handling mouse input */
-       /* Requires put_queue which is static in keyboard.c :-( */
-       /* Only send mouse codes when keyboard is in raw mode. */
-       if (kbd->kbdmode == VC_RAW) {
-               static unsigned char uch_ButtonStateSecond = 0;
-               unsigned char uchButtonSecond;
-
-               /* Send first button, second button and movement. */
-               put_queue( 0x7e );
-               put_queue( data[3] );
-               put_queue( data[4] );
-
-               /* [ACA: Are there any two-button ADB mice that use handler 1 or 2?] */
-
-               /* Store the button state. */
-               uchButtonSecond = (data[4] & 0x80);
-
-               /* Send second button. */
-               if (uchButtonSecond != uch_ButtonStateSecond) {
-                       put_queue( 0x3f | uchButtonSecond );
-                       uch_ButtonStateSecond = uchButtonSecond;
-               }
-
-               /* Macintosh 3-button mouse (handler 4). */
-               if ((nb == 6) && (data[1] & 0x40)) {
-                       static unsigned char uch_ButtonStateThird = 0;
-                       unsigned char uchButtonThird;
-
-                       /* Store the button state for speed. */
-                       uchButtonThird = (data[5] & 0x80);
-
-                       /* Send third button. */
-                       if (uchButtonThird != uch_ButtonStateThird) {
-                               put_queue( 0x40 | uchButtonThird );
-                               uch_ButtonStateThird = uchButtonThird;
-                       }
-               }
-       }
-#endif /* insane MkLinux mouse hack */
-}
-
-/* Map led flags as defined in kbd_kern.h to bits for Apple keyboard. */
-static unsigned char mac_ledmap[8] = {
-    0,         /* none */
-    4,         /* scroll lock */
-    1,         /* num lock */
-    5,         /* scroll + num lock */
-    2,         /* caps lock */
-    6,         /* caps + scroll lock */
-    3,         /* caps + num lock */
-    7,         /* caps + num + scroll lock */
-};
-
-static int leds_pending;
-
-void mac_kbd_leds(unsigned int leds)
-{
-       if (led_request.got_reply) {
-#ifdef DEBUG_ADB
-               if (console_loglevel == 10)
-                       printk("mac_kbd_leds: got reply, sending request!\n");
-#endif
-               adb_request(&led_request, mac_leds_done, 4, ADB_PACKET,
-                            ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG),
-                            0xff, ~mac_ledmap[leds]);
-       } else
-               leds_pending = leds | 0x100;
-}
-
-static void mac_leds_done(struct adb_request *req)
-{
-       int leds;
-
-       if (leds_pending) {
-               leds = leds_pending & 0xff;
-               leds_pending = 0;
-               mac_kbd_leds(leds);
-       }
-       mark_bh(KEYBOARD_BH);
-}
-
-int mac_kbdrate(struct kbd_repeat *k)
-{
-       return 0;
-}
-
-int __init mac_keyb_init(void)
-{
-       static struct adb_request autopoll_req, confcod_req, mouse_req, readkey_req;
-       volatile int ct;
-
-       /* setup key map */
-       memcpy(key_maps[0], mac_plain_map, sizeof(plain_map));
-       memcpy(key_maps[1], mac_shift_map, sizeof(plain_map));
-       memcpy(key_maps[2], mac_altgr_map, sizeof(plain_map));
-       memcpy(key_maps[4], mac_ctrl_map, sizeof(plain_map));
-       memcpy(key_maps[5], mac_shift_ctrl_map, sizeof(plain_map));
-       memcpy(key_maps[8], mac_alt_map, sizeof(plain_map));
-       memcpy(key_maps[12], mac_ctrl_alt_map, sizeof(plain_map));
-
-       /* initialize mouse interrupt hook */
-       adb_mouse_interrupt_hook = NULL;
-
-       /*
-        * Might put that someplace else, possibly ....
-        */
-       adb_bus_init();
-
-       /* the input functions ... */   
-       adb_register(ADB_KEYBOARD, keyboard_input);
-       adb_register(ADB_MOUSE, mouse_input);
-
-       /* turn on ADB auto-polling in the CUDA */
-       
-       /*
-        *      Older boxes don't support CUDA_* targets and CUDA commands
-        *      instead we emulate them in the adb_request hook to make
-        *      the code interfaces saner.
-        *
-        *      Note XXX: the Linux PMac and this code both assume the
-        *      devices are at their primary ids and do not do device
-        *      assignment. This isn't ideal. We should fix it to follow
-        *      the reassignment specs.
-        */
-
-       if (macintosh_config->adb_type == MAC_ADB_CUDA) {
-               printk("CUDA autopoll on ...\n");
-               adb_request(&autopoll_req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1);
-               ct=0; 
-               while (!autopoll_req.got_reply && ++ct<1000)
-               {
-                               udelay(10);
-               }
-               if(ct==1000) {
-                       printk("Keyboard timed out.\n");
-                       autopoll_req.got_reply = 1;
-               }
-       }
-
-       /*
-        *      XXX: all ADB requests now in CUDA format; adb_request takes 
-        *      care of that for other Macs.
-        */
-
-       printk("Configuring keyboard:\n");
-
-       udelay(3000);
-
-       /* 
-        * turn on all leds - the keyboard driver will turn them back off 
-        * via mac_kbd_leds if everything works ok!
-        */
-       printk("leds on ...\n");
-       adb_request(&led_request, NULL, 4, ADB_PACKET,
-                    ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG), 0xff, ~7);
-
-       /*
-        * The polling stuff should go away as soon as the ADB driver is stable
-        */
-       ct = 0;
-       while (!led_request.got_reply && ++ct<1000)
-       {
-               udelay(10);
-       }
-       if(ct==1000) {
-               printk("keyboard timed out.\n");
-               led_request.got_reply  = 1;
-       }
-
-#if 1
-       printk("configuring coding mode ...\n");
-
-       udelay(3000);
-
-       /* 
-        * get the keyboard to send separate codes for
-        * left and right shift, control, option keys. 
-        */
-       adb_request(&confcod_req, NULL, 4, ADB_PACKET, 
-                    ADB_WRITEREG(ADB_KEYBOARD, 3), 0, 3);
-
-       ct=0; 
-       while (!confcod_req.got_reply && ++ct<1000)
-       {
-               udelay(10);
-       }
-       if(ct==1000) {
-               printk("keyboard timed out.\n");
-               confcod_req.got_reply  = 1;
-       }
-#endif
-
-#if 0  /* seems to hurt, at least Geert's Mac */
-       printk("Configuring mouse (3-button mode) ...\n");
-
-       udelay(3000);
-
-       /* 
-        * XXX: taken from the PPC driver again ... 
-        * Try to switch the mouse (id 3) to handler 4, for three-button
-        * mode. (0x20 is Service Request Enable, 0x03 is Device ID). 
-        */
-       adb_request(&mouse_req, NULL, 4, ADB_PACKET,
-                   ADB_WRITEREG(ADB_MOUSE, 3), 0x23, 4 );
-
-       ct=0; 
-       while (!mouse_req.got_reply && ++ct<1000)
-       {
-               udelay(10);
-       }
-       if(ct==1000)
-               printk("Mouse timed out.\n");
-#endif
-
-#if 0
-       printk("Start polling keyboard ...\n");
-
-       /* 
-        *      get the keyboard to send data back, via the adb_input hook
-        *      XXX: was never used properly, and the driver takes care
-        *      of polling and timeout retransmits now.
-        *      Might be of use if we want to start talking to a specific
-        *      device here...
-        */
-       adb_request(&readkey_req, NULL, 2, ADB_PACKET, 
-                    ADB_READREG(ADB_KEYBOARD, KEYB_KEYREG));
-#endif
-
-       in_keybinit = 0;
-       printk("keyboard init done\n");
-
-       return 0;
-}
diff --git a/arch/m68k/mac/misc.c b/arch/m68k/mac/misc.c
new file mode 100644 (file)
index 0000000..15803f9
--- /dev/null
@@ -0,0 +1,689 @@
+/*
+ * Miscellaneous Mac68K-specific stuff 
+ */
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/kd.h>
+#include <linux/mm.h>
+
+#include <linux/adb.h>
+#include <linux/cuda.h>
+#include <linux/pmu.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/setup.h>
+#include <asm/macintosh.h>
+#include <asm/mac_via.h>
+#include <asm/mac_oss.h>
+
+#define BOOTINFO_COMPAT_1_0
+#include <asm/setup.h>
+#include <asm/bootinfo.h>
+#include <asm/machdep.h>
+
+/* Offset between Unix time (1970-based) and Mac time (1904-based) */
+
+#define RTC_OFFSET 2082844800
+
+extern struct mac_booter_data mac_bi_data;
+static void (*rom_reset)(void);
+
+/*
+ * Return the current time as the number of seconds since January 1, 1904.
+ */
+
+static long adb_read_time(void)
+{
+       volatile struct adb_request req;
+       long time;
+
+       adb_request((struct adb_request *) &req, NULL,
+                       ADBREQ_RAW|ADBREQ_SYNC,
+                       2, CUDA_PACKET, CUDA_GET_TIME);
+
+       time = (req.reply[3] << 24) | (req.reply[4] << 16)
+               | (req.reply[5] << 8) | req.reply[6];
+       return time - RTC_OFFSET;
+}
+
+/*
+ * Set the current system time
+ */
+
+static void adb_write_time(long data)
+{
+       volatile struct adb_request req;
+
+       data += RTC_OFFSET;
+       
+       adb_request((struct adb_request *) &req, NULL,
+                       ADBREQ_RAW|ADBREQ_SYNC,
+                       6, CUDA_PACKET, CUDA_SET_TIME,
+                       (data >> 24) & 0xFF, (data >> 16) & 0xFF,
+                       (data >> 8) & 0xFF, data & 0xFF);
+}
+
+/*
+ * Get a byte from the NVRAM
+ */
+
+static __u8 adb_read_pram(int offset)
+{
+       volatile struct adb_request req;
+
+       adb_request((struct adb_request *) &req, NULL,
+                       ADBREQ_RAW|ADBREQ_SYNC,
+                       4, CUDA_PACKET, CUDA_GET_PRAM,
+                       (offset >> 8) & 0xFF, offset & 0xFF);
+       return req.reply[3];
+}
+
+/*
+ * Write a byte to the NVRAM
+ */
+
+static void adb_write_pram(int offset, __u8 data)
+{
+       volatile struct adb_request req;
+
+       adb_request((struct adb_request *) &req, NULL,
+                       ADBREQ_RAW|ADBREQ_SYNC,
+                       5, CUDA_PACKET, CUDA_SET_PRAM,
+                       (offset >> 8) & 0xFF, offset & 0xFF,
+                       data);
+}
+
+/*
+ * VIA PRAM/RTC access routines
+ *
+ * Must be called with interrupts disabled and
+ * the RTC should be enabled.
+ */
+
+static __u8 via_pram_readbyte(void)
+{
+       int     i,reg;
+       __u8    data;
+
+       reg = via1[vBufB] & ~VIA1B_vRTCClk;
+
+       /* Set the RTC data line to be an input. */
+
+       via1[vDirB] &= ~VIA1B_vRTCData;
+
+       /* The bits of the byte come out in MSB order */
+
+       data = 0;
+       for (i = 0 ; i < 8 ; i++) {
+               via1[vBufB] = reg;
+               via1[vBufB] = reg | VIA1B_vRTCClk;
+               data = (data << 1) | (via1[vBufB] & VIA1B_vRTCData);
+       }
+
+       /* Return RTC data line to output state */
+
+       via1[vDirB] |= VIA1B_vRTCData;
+
+       return data;
+}
+
+static void via_pram_writebyte(__u8 data)
+{
+       int     i,reg,bit;
+
+       reg = via1[vBufB] & ~(VIA1B_vRTCClk | VIA1B_vRTCData);
+
+       /* The bits of the byte go in in MSB order */
+
+       for (i = 0 ; i < 8 ; i++) {
+               bit = data & 0x80? 1 : 0;
+               data <<= 1;
+               via1[vBufB] = reg | bit;
+               via1[vBufB] = reg | bit | VIA1B_vRTCClk;
+       }
+}
+
+/*
+ * Execute a VIA PRAM/RTC command. For read commands
+ * data should point to a one-byte buffer for the
+ * resulting data. For write commands it should point
+ * to the data byte to for the command.
+ *
+ * This function disables all interrupts while running.
+ */
+
+static void via_pram_command(int command, __u8 *data)
+{
+       unsigned long cpu_flags;
+       int     is_read;
+
+       save_flags(cpu_flags);
+       cli();
+
+       /* Enable the RTC and make sure the strobe line is high */
+
+       via1[vBufB] = (via1[vBufB] | VIA1B_vRTCClk) & ~VIA1B_vRTCEnb;
+
+       if (command & 0xFF00) {         /* extended (two-byte) command */
+               via_pram_writebyte((command & 0xFF00) >> 8);
+               via_pram_writebyte(command & 0xFF);
+               is_read = command & 0x8000;
+       } else {                        /* one-byte command */
+               via_pram_writebyte(command);
+               is_read = command & 0x80;
+       }
+       if (is_read) {
+               *data = via_pram_readbyte();
+       } else {
+               via_pram_writebyte(*data);
+       }
+
+       /* All done, disable the RTC */
+
+       via1[vBufB] |= VIA1B_vRTCEnb;
+
+       restore_flags(cpu_flags);
+}
+
+static __u8 via_read_pram(int offset)
+{
+       return 0;
+}
+
+static void via_write_pram(int offset, __u8 data)
+{
+}
+
+/*
+ * Return the current time in seconds since January 1, 1904.
+ *
+ * This only works on machines with the VIA-based PRAM/RTC, which
+ * is basically any machine with Mac II-style ADB.
+ */
+
+static long via_read_time(void)
+{
+       union {
+               __u8  cdata[4];
+               long  idata;
+       } result, last_result;
+       int     ct;
+
+       /*
+        * The NetBSD guys say to loop until you get the same reading
+        * twice in a row.
+        */
+
+       ct = 0;
+       do {
+               if (++ct > 10) {
+                       printk("via_read_time: couldn't get valid time, "
+                              "last read = 0x%08X and 0x%08X\n", last_result.idata,
+                              result.idata);
+                       break;
+               }
+
+               last_result.idata = result.idata;
+               result.idata = 0;
+
+               via_pram_command(0x81, &result.cdata[3]);
+               via_pram_command(0x85, &result.cdata[2]);
+               via_pram_command(0x89, &result.cdata[1]);
+               via_pram_command(0x8D, &result.cdata[0]);
+       } while (result.idata != last_result.idata);
+
+       return result.idata - RTC_OFFSET;
+}
+
+/*
+ * Set the current time to a number of seconds since January 1, 1904.
+ *
+ * This only works on machines with the VIA-based PRAM/RTC, which
+ * is basically any machine with Mac II-style ADB.
+ */
+
+static void via_write_time(long time)
+{
+       union {
+               __u8  cdata[4];
+               long  idata;
+       } data;
+       __u8    temp;
+
+       /* Clear the write protect bit */
+
+       temp = 0x55;
+       via_pram_command(0x35, &temp);
+
+       data.idata = time + RTC_OFFSET;
+       via_pram_command(0x01, &data.cdata[3]);
+       via_pram_command(0x05, &data.cdata[2]);
+       via_pram_command(0x09, &data.cdata[1]);
+       via_pram_command(0x0D, &data.cdata[0]);
+
+       /* Set the write protect bit */
+
+       temp = 0xD5;
+       via_pram_command(0x35, &temp);
+}
+
+static void via_shutdown(void)
+{
+       if (rbv_present) {
+               via2[rBufB] &= ~0x04;
+       } else {
+               /* Direction of vDirB is output */
+               via2[vDirB] |= 0x04;
+               /* Send a value of 0 on that line */
+               via2[vBufB] &= ~0x04;
+               mdelay(1000);
+       }
+}
+
+/*
+ * FIXME: not sure how this is supposed to work exactly...
+ */
+
+static void oss_shutdown(void)
+{
+       oss->rom_ctrl = OSS_POWEROFF;
+}
+
+#ifdef CONFIG_ADB_CUDA
+
+static void cuda_restart(void)
+{
+       adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+                       2, CUDA_PACKET, CUDA_RESET_SYSTEM);
+}
+
+static void cuda_shutdown(void)
+{
+       adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+                       2, CUDA_PACKET, CUDA_POWERDOWN);
+}
+
+#endif /* CONFIG_ADB_CUDA */
+
+#ifdef CONFIG_ADB_PMU
+
+void pmu_restart(void)
+{
+       adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+                       3, PMU_PACKET, PMU_SET_INTR_MASK,
+                       PMU_INT_ADB|PMU_INT_TICK);
+       
+       adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+                       2, PMU_PACKET, PMU_RESET);
+}
+
+void pmu_shutdown(void)
+{
+       adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+                       3, PMU_PACKET, PMU_SET_INTR_MASK,
+                       PMU_INT_ADB|PMU_INT_TICK);
+
+       adb_request(NULL, NULL, ADBREQ_RAW|ADBREQ_SYNC,
+                       6, PMU_PACKET, PMU_SHUTDOWN,
+                       'M', 'A', 'T', 'T');
+}
+
+#endif /* CONFIG_ADB_PMU */
+
+/*
+ *-------------------------------------------------------------------
+ * Below this point are the generic routines; they'll dispatch to the
+ * correct routine for the hardware on which we're running.
+ *-------------------------------------------------------------------
+ */
+
+void mac_pram_read(int offset, __u8 *buffer, int len)
+{
+       __u8 (*func)(int) = NULL;
+       int i;
+
+       if (macintosh_config->adb_type == MAC_ADB_IISI ||
+           macintosh_config->adb_type == MAC_ADB_PB1 ||
+           macintosh_config->adb_type == MAC_ADB_PB2 ||
+           macintosh_config->adb_type == MAC_ADB_CUDA) {
+               func = adb_read_pram;
+       } else {
+               func = via_read_pram;
+       }
+       for (i = 0 ; i < len ; i++) {
+               buffer[i] = (*func)(offset++);
+       }
+}
+
+void mac_pram_write(int offset, __u8 *buffer, int len)
+{
+       void (*func)(int, __u8) = NULL;
+       int i;
+
+       if (macintosh_config->adb_type == MAC_ADB_IISI ||
+           macintosh_config->adb_type == MAC_ADB_PB1 ||
+           macintosh_config->adb_type == MAC_ADB_PB2 ||
+           macintosh_config->adb_type == MAC_ADB_CUDA) {
+               func = adb_write_pram;
+       } else {
+               func = via_write_pram;
+       }
+       for (i = 0 ; i < len ; i++) {
+               (*func)(offset++, buffer[i]);
+       }
+}
+
+void mac_poweroff(void)
+{
+       /*
+        * MAC_ADB_IISI may need to be moved up here if it doesn't actually
+        * work using the ADB packet method.  --David Kilzer
+        */
+
+       if (oss_present) {
+               oss_shutdown();
+       } else if (macintosh_config->adb_type == MAC_ADB_II) {
+               via_shutdown();
+#ifdef CONFIG_ADB_CUDA
+       } else if (macintosh_config->adb_type == MAC_ADB_CUDA) {
+               cuda_shutdown();
+#endif
+#ifdef CONFIG_ADB_PMU
+       } else if (macintosh_config->adb_type == MAC_ADB_PB1
+               || macintosh_config->adb_type == MAC_ADB_PB2) {
+               pmu_shutdown();
+#endif
+       }
+       sti();
+       printk("It is now safe to turn off your Macintosh.\n");
+       while(1);
+}
+
+void mac_reset(void)
+{
+       if (macintosh_config->adb_type == MAC_ADB_II) {
+               unsigned long cpu_flags;
+
+               /* need ROMBASE in booter */
+               /* indeed, plus need to MAP THE ROM !! */
+
+               if (mac_bi_data.rombase == 0)
+                       mac_bi_data.rombase = 0x40800000;
+
+               /* works on some */
+               rom_reset = (void *) (mac_bi_data.rombase + 0xa);
+
+               if (macintosh_config->ident == MAC_MODEL_SE30) {
+                       /*
+                        * MSch: Machines known to crash on ROM reset ...
+                        */
+               } else {
+                       save_flags(cpu_flags);
+                       cli();
+
+                       rom_reset();
+
+                       restore_flags(cpu_flags);
+               }
+#ifdef CONFIG_ADB_CUDA
+       } else if (macintosh_config->adb_type == MAC_ADB_CUDA) {
+               cuda_restart();
+#endif
+#ifdef CONFIG_ADB_PMU
+       } else if (macintosh_config->adb_type == MAC_ADB_PB1
+               || macintosh_config->adb_type == MAC_ADB_PB2) {
+               pmu_restart();
+#endif
+       } else if (CPU_IS_030) {
+
+               /* 030-specific reset routine.  The idea is general, but the
+                * specific registers to reset are '030-specific.  Until I
+                * have a non-030 machine, I can't test anything else.
+                *  -- C. Scott Ananian <cananian@alumni.princeton.edu>
+                */
+
+               unsigned long rombase = 0x40000000;
+
+               /* make a 1-to-1 mapping, using the transparent tran. reg. */
+               unsigned long virt = (unsigned long) mac_reset;
+               unsigned long phys = virt_to_phys(mac_reset);
+               unsigned long offset = phys-virt;
+               cli(); /* lets not screw this up, ok? */
+               __asm__ __volatile__(".chip 68030\n\t"
+                                    "pmove %0,%/tt0\n\t"
+                                    ".chip 68k"
+                                    : : "m" ((phys&0xFF000000)|0x8777));
+               /* Now jump to physical address so we can disable MMU */
+               __asm__ __volatile__(
+                    ".chip 68030\n\t"
+                   "lea %/pc@(1f),%/a0\n\t"
+                   "addl %0,%/a0\n\t"/* fixup target address and stack ptr */
+                   "addl %0,%/sp\n\t" 
+                   "pflusha\n\t"
+                   "jmp %/a0@\n\t" /* jump into physical memory */
+                   "0:.long 0\n\t" /* a constant zero. */
+                   /* OK.  Now reset everything and jump to reset vector. */
+                   "1:\n\t"
+                   "lea %/pc@(0b),%/a0\n\t"
+                   "pmove %/a0@, %/tc\n\t" /* disable mmu */
+                   "pmove %/a0@, %/tt0\n\t" /* disable tt0 */
+                   "pmove %/a0@, %/tt1\n\t" /* disable tt1 */
+                   "movel #0, %/a0\n\t"
+                   "movec %/a0, %/vbr\n\t" /* clear vector base register */
+                   "movec %/a0, %/cacr\n\t" /* disable caches */
+                   "movel #0x0808,%/a0\n\t"
+                   "movec %/a0, %/cacr\n\t" /* flush i&d caches */
+                   "movew #0x2700,%/sr\n\t" /* set up status register */
+                   "movel %1@(0x0),%/a0\n\t"/* load interrupt stack pointer */
+                   "movec %/a0, %/isp\n\t" 
+                   "movel %1@(0x4),%/a0\n\t" /* load reset vector */
+                   "reset\n\t" /* reset external devices */
+                   "jmp %/a0@\n\t" /* jump to the reset vector */
+                   ".chip 68k"
+                   : : "r" (offset), "a" (rombase) : "a0");
+       }
+
+       /* should never get here */
+       sti();
+       printk ("Restart failed.  Please restart manually.\n");
+       while(1);
+}
+
+/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
+ * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
+ * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
+ *
+ * [For the Julian calendar (which was used in Russia before 1917,
+ * Britain & colonies before 1752, anywhere else before 1582,
+ * and is still in use by some communities) leave out the
+ * -year/100+year/400 terms, and add 10.]
+ *
+ * This algorithm was first published by Gauss (I think).
+ *
+ * WARNING: this function will overflow on 2106-02-07 06:28:16 on
+ * machines were long is 32-bit! (However, as time_t is signed, we
+ * will already get problems at other places on 2038-01-19 03:14:08)
+ */
+static unsigned long mktime(unsigned int year, unsigned int mon,
+       unsigned int day, unsigned int hour,
+       unsigned int min, unsigned int sec)
+{
+       if (0 >= (int) (mon -= 2)) {    /* 1..12 -> 11,12,1..10 */
+               mon += 12;      /* Puts Feb last since it has leap day */
+               year -= 1;
+       }
+       return (((
+           (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
+             year*365 - 719499
+           )*24 + hour /* now have hours */
+          )*60 + min /* now have minutes */
+         )*60 + sec; /* finally seconds */
+}
+
+/*
+ * This function translates seconds since 1970 into a proper date.
+ *
+ * Algorithm cribbed from glibc2.1, __offtime().
+ */
+#define SECS_PER_MINUTE (60)
+#define SECS_PER_HOUR  (SECS_PER_MINUTE * 60)
+#define SECS_PER_DAY   (SECS_PER_HOUR * 24)
+
+static void unmktime(unsigned long time, long offset,
+                    int *yearp, int *monp, int *dayp,
+                    int *hourp, int *minp, int *secp)
+{
+        /* How many days come before each month (0-12).  */
+       static const unsigned short int __mon_yday[2][13] =
+       {
+               /* Normal years.  */
+               { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+               /* Leap years.  */
+               { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+       };
+       long int days, rem, y, wday, yday;
+       const unsigned short int *ip;
+
+       days = time / SECS_PER_DAY;
+       rem = time % SECS_PER_DAY;
+       rem += offset;
+       while (rem < 0) {
+               rem += SECS_PER_DAY;
+               --days;
+       }
+       while (rem >= SECS_PER_DAY) {
+               rem -= SECS_PER_DAY;
+               ++days;
+       }
+       *hourp = rem / SECS_PER_HOUR;
+       rem %= SECS_PER_HOUR;
+       *minp = rem / SECS_PER_MINUTE;
+       *secp = rem % SECS_PER_MINUTE;
+       /* January 1, 1970 was a Thursday. */
+       wday = (4 + days) % 7; /* Day in the week. Not currently used */
+       if (wday < 0) wday += 7;
+       y = 1970;
+
+#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
+#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
+#define __isleap(year) \
+  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+
+       while (days < 0 || days >= (__isleap (y) ? 366 : 365))
+       {
+               /* Guess a corrected year, assuming 365 days per year.  */
+               long int yg = y + days / 365 - (days % 365 < 0);
+
+               /* Adjust DAYS and Y to match the guessed year.  */
+               days -= ((yg - y) * 365
+                        + LEAPS_THRU_END_OF (yg - 1)
+                        - LEAPS_THRU_END_OF (y - 1));
+               y = yg;
+       }
+       *yearp = y - 1900;
+       yday = days; /* day in the year.  Not currently used. */
+       ip = __mon_yday[__isleap(y)];
+       for (y = 11; days < (long int) ip[y]; --y)
+               continue;
+       days -= ip[y];
+       *monp = y;
+       *dayp = days + 1; /* day in the month */
+       return;
+}
+
+/*
+ * Return the boot time for use in initializing the kernel clock.
+ *
+ * I'd like to read the hardware clock here but many machines read
+ * the PRAM through ADB, and interrupts aren't initialized when this
+ * is called so ADB obviously won't work.
+ */
+
+void mac_gettod(int *yearp, int *monp, int *dayp,
+              int *hourp, int *minp, int *secp)
+{
+       /* Yes the GMT bias is backwards.  It looks like Penguin is
+           screwing up the boottime it gives us... This works for me
+           in Canada/Eastern but it might be wrong everywhere else. */
+       unmktime(mac_bi_data.boottime, -mac_bi_data.gmtbias * 60,
+               yearp, monp, dayp, hourp, minp, secp);
+       /* For some reason this is off by one */
+       *monp = *monp + 1;
+}
+
+/* 
+ * Read/write the hardware clock.
+ */
+
+int mac_hwclk(int op, struct hwclk_time *t)
+{
+       unsigned long now;
+
+       if (!op) { /* read */
+               if (macintosh_config->adb_type == MAC_ADB_II) {
+                       now = via_read_time();
+               } else if ((macintosh_config->adb_type == MAC_ADB_IISI) ||
+                          (macintosh_config->adb_type == MAC_ADB_PB1) ||
+                          (macintosh_config->adb_type == MAC_ADB_PB2) ||
+                          (macintosh_config->adb_type == MAC_ADB_CUDA)) {
+                       now = adb_read_time();
+               } else if (macintosh_config->adb_type == MAC_ADB_IOP) {
+                       now = via_read_time();
+               } else {
+                       now = 0;
+               }
+
+               t->wday = 0;
+               unmktime(now, 0,
+                        &t->year, &t->mon, &t->day,
+                        &t->hour, &t->min, &t->sec);
+               printk("mac_hwclk: read %04d-%02d-%-2d %02d:%02d:%02d\n",
+                       t->year + 1900, t->mon + 1, t->day, t->hour, t->min, t->sec);
+       } else { /* write */
+               printk("mac_hwclk: tried to write %04d-%02d-%-2d %02d:%02d:%02d\n",
+                       t->year + 1900, t->mon + 1, t->day, t->hour, t->min, t->sec);
+
+#if 0  /* it trashes my rtc */
+               now = mktime(t->year + 1900, t->mon + 1, t->day,
+                            t->hour, t->min, t->sec);
+
+               if (macintosh_config->adb_type == MAC_ADB_II) {
+                       via_write_time(now);
+               } else if ((macintosh_config->adb_type == MAC_ADB_IISI) ||
+                          (macintosh_config->adb_type == MAC_ADB_PB1) ||
+                          (macintosh_config->adb_type == MAC_ADB_PB2) ||
+                          (macintosh_config->adb_type == MAC_ADB_CUDA)) {
+                       adb_write_time(now);
+               } else if (macintosh_config->adb_type == MAC_ADB_IOP) {
+                       via_write_time(now);
+               }
+#endif
+       }
+       return 0;
+}
+
+/*
+ * Set minutes/seconds in the hardware clock
+ */
+
+int mac_set_clock_mmss (unsigned long nowtime)
+{
+       struct hwclk_time now;
+
+       mac_hwclk(0, &now);
+       now.sec = nowtime % 60;
+       now.min = (nowtime / 60) % 60;
+       mac_hwclk(1, &now);
+
+       return 0;
+}
index 442d72802b6b7f6b3ae65bf2266b6862ddef0bb2..c405fc0dad094571cd9c0feaa716ec0a1ef8f521 100644 (file)
@@ -34,7 +34,7 @@ void oss_irq(int, void *, struct pt_regs *);
 void oss_nubus_irq(int, void *, struct pt_regs *);
 
 extern void via1_irq(int, void *, struct pt_regs *);
-extern void mac_SCC_handler(int, void *, struct pt_regs *);
+extern void mac_scc_dispatch(int, void *, struct pt_regs *);
 extern int console_loglevel;
 
 /*
@@ -68,16 +68,16 @@ void __init oss_init(void)
 
 void __init oss_register_interrupts(void)
 {
-       request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK,
-                   "OSS SCSI Dispatch", (void *) oss);
-       request_irq(OSS_IRQLEV_IOPSCC, mac_SCC_handler, IRQ_FLG_LOCK,
-                   "SCC Dispatch", mac_SCC_handler);
-       request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK,
-                   "Nubus Dispatch", (void *) oss);
-       request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK,
-                   "OSS Sound Dispatch", (void *) oss);
-       request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK,
-                   "VIA1 Dispatch", (void *) via1);
+       sys_request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK,
+                       "scsi", (void *) oss);
+       sys_request_irq(OSS_IRQLEV_IOPSCC, mac_scc_dispatch, IRQ_FLG_LOCK,
+                       "scc", mac_scc_dispatch);
+       sys_request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK,
+                       "nubus", (void *) oss);
+       sys_request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK,
+                       "sound", (void *) oss);
+       sys_request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK,
+                       "via1", (void *) via1);
 }
 
 /*
@@ -88,22 +88,6 @@ void __init oss_nubus_init(void)
 {
 }
 
-/*
- * Turn off the power via the ROM control register
- *
- * FIXME: not sure how this is supposed to work exactly...
- */
-
-void oss_poweroff(void)
-{
-       oss->rom_ctrl = OSS_POWEROFF;
-
-       /* We should never make it this far... */
-
-       printk ("It is now safe to switch off your machine.\n");
-       while(1);
-}
-
 /*
  * Handle miscellaneous OSS interrupts. Right now that's just sound
  * and SCSI; everything else is routed to its own autovector IRQ.
index 45825b11318714c4e03582dc9f02f8a485873ac9..86bbf72cc90cd670c780bfd8ba3fcde1833ef13b 100644 (file)
@@ -119,14 +119,14 @@ void __init psc_init(void)
 
 void __init psc_register_interrupts(void)
 {
-       request_irq(3, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch",
-                   (void *) 0x30);
-       request_irq(4, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch",
-                   (void *) 0x40);
-       request_irq(5, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch",
-                   (void *) 0x50);
-       request_irq(6, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch",
-                   (void *) 0x60);
+       sys_request_irq(3, psc_irq, IRQ_FLG_LOCK, "psc3",
+                       (void *) 0x30);
+       sys_request_irq(4, psc_irq, IRQ_FLG_LOCK, "psc4",
+                       (void *) 0x40);
+       sys_request_irq(5, psc_irq, IRQ_FLG_LOCK, "psc5",
+                       (void *) 0x50);
+       sys_request_irq(6, psc_irq, IRQ_FLG_LOCK, "psc6",
+                       (void *) 0x60);
 }
 
 /*
index 2205b226c63abce660ff944e4e8e1c336ff7590f..af001907d828707b1947f6a037210af74dccaf22 100644 (file)
@@ -24,6 +24,8 @@
 #include <linux/delay.h>
 #include <linux/init.h>
 
+#include <linux/ide.h>
+
 #include <asm/traps.h>
 #include <asm/bootinfo.h> 
 #include <asm/macintosh.h> 
@@ -60,7 +62,7 @@ static int gIER,gIFR,gBufA,gBufB;
 #define MAC_CLOCK_LOW          (MAC_CLOCK_TICK&0xFF)
 #define MAC_CLOCK_HIGH         (MAC_CLOCK_TICK>>8)
 
-static int  nubus_active;
+static int  nubus_active = 0;
 
 void via_debug_dump(void);
 void via1_irq(int, void *, struct pt_regs *);
@@ -71,7 +73,7 @@ void via_irq_disable(int irq);
 void via_irq_clear(int irq);
 
 extern void mac_bang(int, void *, struct pt_regs *);
-extern void mac_SCC_handler(int, void *, struct pt_regs *);
+extern void mac_scc_dispatch(int, void *, struct pt_regs *);
 extern int console_loglevel;
 extern int oss_present;
 
@@ -260,28 +262,28 @@ void __init via_init_clock(void (*func)(int, void *, struct pt_regs *))
 void __init via_register_interrupts(void)
 {
        if (via_alt_mapping) {
-               request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
-                           "Software IRQ", (void *) via1);
-               request_irq(IRQ_AUTO_6, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
-                           "VIA1 Dispatch", (void *) via1);
+               sys_request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+                               "software", (void *) via1);
+               sys_request_irq(IRQ_AUTO_6, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+                               "via1", (void *) via1);
        } else {
-               request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
-                           "VIA1 Dispatch", (void *) via1);
+               sys_request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+                               "via1", (void *) via1);
 #if 0 /* interferes with serial on some machines */
                if (!psc_present) {
-                       request_irq(IRQ_AUTO_6, mac_bang, IRQ_FLG_LOCK,
+                       sys_request_irq(IRQ_AUTO_6, mac_bang, IRQ_FLG_LOCK,
                                        "Off Switch", mac_bang);
                }
 #endif
        }
-       request_irq(IRQ_AUTO_2, via2_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
-                   "VIA2 Dispatch", (void *) via2);
+       sys_request_irq(IRQ_AUTO_2, via2_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
+                       "via2", (void *) via2);
        if (!psc_present) {
-               request_irq(IRQ_AUTO_4, mac_SCC_handler, IRQ_FLG_LOCK,
-                           "SCC Dispatch", mac_SCC_handler);
+               sys_request_irq(IRQ_AUTO_4, mac_scc_dispatch, IRQ_FLG_LOCK,
+                               "scc", mac_scc_dispatch);
        }
        request_irq(IRQ_MAC_NUBUS, via_nubus_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST,
-                       "Nubus Dispatch", (void *) via2);
+                       "nubus", (void *) via2);
 }
 
 /*
@@ -360,35 +362,14 @@ int via_get_cache_disable(void)
        return (int) via2[gBufB] & VIA2B_vCDis;
 }
 
-/*
- * VIA-based power switch, for machines that support it.
- */
-
-void via_poweroff(void)
-{
-       if (rbv_present) {
-               via2[rBufB] &= ~0x04;
-       } else {
-               /* Direction of vDirB is output */
-               via2[vDirB] |= 0x04;
-               /* Send a value of 0 on that line */
-               via2[vBufB] &= ~0x04;
-               /* Otherwise it prints "It is now.." then shuts off */
-               mdelay(1000);
-       }
-
-       /* We should never make it this far... */
-       printk ("It is now safe to switch off your machine.\n");
-       while(1);
-}
-
 /*
  * Initialize VIA2 for Nubus access
  */
 
 void __init via_nubus_init(void)
 {
-       nubus_active = 0;
+       /* don't set nubus_active = 0 here, it kills the Baboon */
+       /* interrupt that we've already registered.             */
 
        /* unlock nubus transactions */
 
@@ -396,14 +377,22 @@ void __init via_nubus_init(void)
                /* set the line to be an output on non-RBV machines */
                via2[vDirB] |= 0x02;
        }
-       via2[gBufB] |= 0x02;
+
+       /* this seems to be an ADB bit on PMU machines */
+       /* according to MkLinux.  -- jmt               */
+
+       if ((macintosh_config->adb_type != MAC_ADB_PB1) &&
+           (macintosh_config->adb_type != MAC_ADB_PB2)) {
+               via2[gBufB] |= 0x02;
+       }
 
        /* disable nubus slot interrupts. */
        if (rbv_present) {
-               via2[rSIER] = 0x7F;     /* like VIA; bit 7=clr,set */
+               via2[rSIER] = 0x7F;
+               via2[rSIER] = nubus_active | 0x80;
        } else {
-               via2[vBufA] = 0xFF;     /* active low irqs, force high */
-               via2[vDirA] = 0xFF;     /* ddr to output. */
+               via2[vBufA] = 0xFF;
+               via2[vDirA] = ~nubus_active;
        }
 }
 
@@ -478,7 +467,7 @@ void via_nubus_irq(int irq, void *dev_id, struct pt_regs *regs)
        int irq_bit, i;
        unsigned char events;
 
-       if (!(events = ~via2[gBufA]  & nubus_active)) return;
+       if (!(events = ~via2[gBufA] & nubus_active)) return;
 
        for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1) {
                if (events & irq_bit) {
@@ -587,188 +576,3 @@ int via_irq_pending(int irq)
        }
        return 0;
 }
-
-void via_scsi_clear(void)
-{
-       volatile unsigned char deep_magic;
-
-#ifdef DEBUG_IRQUSE
-       printk("via_scsi_clear()\n");
-#endif
-
-       /* We handle this in oss.c , but this gets called in mac_scsinew.c */
-       if(oss_present) return;
-
-       if (rbv_present) {
-               via2[rIFR] = (1<<3) | (1<<0) | rbv_clear;
-               deep_magic = via2[rBufB];
-       } else {
-               deep_magic = via2[vBufB];
-       }
-       mac_enable_irq(IRQ_MAC_SCSI);
-}
-
-/*
- * PRAM/RTC access routines
- *
- * Must be called with interrupts disabled and
- * the RTC should be enabled.
- */
-
-static __u8 via_pram_readbyte(void)
-{
-       int     i,reg;
-       __u8    data;
-
-       reg = via1[vBufB] & ~VIA1B_vRTCClk;
-
-       /* Set the RTC data line to be an input. */
-
-       via1[vDirB] &= ~VIA1B_vRTCData;
-
-       /* The bits of the byte come out in MSB order */
-
-       data = 0;
-       for (i = 0 ; i < 8 ; i++) {
-               via1[vBufB] = reg;
-               via1[vBufB] = reg | VIA1B_vRTCClk;
-               data = (data << 1) | (via1[vBufB] & VIA1B_vRTCData);
-       }
-
-       /* Return RTC data line to output state */
-
-       via1[vDirB] |= VIA1B_vRTCData;
-
-       return data;
-}
-
-static void via_pram_writebyte(__u8 data)
-{
-       int     i,reg,bit;
-
-       reg = via1[vBufB] & ~(VIA1B_vRTCClk | VIA1B_vRTCData);
-
-       /* The bits of the byte go in in MSB order */
-
-       for (i = 0 ; i < 8 ; i++) {
-               bit = data & 0x80? 1 : 0;
-               data <<= 1;
-               via1[vBufB] = reg | bit;
-               via1[vBufB] = reg | bit | VIA1B_vRTCClk;
-       }
-}
-
-/*
- * Execute a PRAM/RTC command. For read commands
- * data should point to a one-byte buffer for the
- * resulting data. For write commands it should point
- * to the data byte to for the command.
- *
- * This function disables all interrupts while running.
- */
-
-void via_pram_command(int command, __u8 *data)
-{
-       unsigned long cpu_flags;
-       int     is_read;
-
-       save_flags(cpu_flags);
-       cli();
-
-       /* Enable the RTC and make sure the strobe line is high */
-
-       via1[vBufB] = (via1[vBufB] | VIA1B_vRTCClk) & ~VIA1B_vRTCEnb;
-
-       if (command & 0xFF00) {         /* extended (two-byte) command */
-               via_pram_writebyte((command & 0xFF00) >> 8);
-               via_pram_writebyte(command & 0xFF);
-               is_read = command & 0x8000;
-       } else {                        /* one-byte command */
-               via_pram_writebyte(command);
-               is_read = command & 0x80;
-       }
-       if (is_read) {
-               *data = via_pram_readbyte();
-       } else {
-               via_pram_writebyte(*data);
-       }
-
-       /* All done, disable the RTC */
-
-       via1[vBufB] |= VIA1B_vRTCEnb;
-
-       restore_flags(cpu_flags);
-}
-
-/*
- * Return the current time in seconds since January 1, 1904.
- *
- * This only works on machines with the VIA-based PRAM/RTC, which
- * is basically any machine with Mac II-style ADB.
- */
-
-__u32 via_read_time(void)
-{
-       union {
-               __u8  cdata[4];
-               __u32 idata;
-       } result, last_result;
-       int     ct;
-
-       /*
-        * The NetBSD guys say to loop until you get the same reading
-        * twice in a row.
-        */
-
-       ct = 0;
-       do {
-               if (++ct > 10) {
-                       printk("via_read_time: couldn't get valid time, "
-                              "last read = 0x%08X and 0x%08X\n", last_result.idata,
-                              result.idata);
-                       break;
-               }
-
-               last_result.idata = result.idata;
-               result.idata = 0;
-
-               via_pram_command(0x81, &result.cdata[3]);
-               via_pram_command(0x85, &result.cdata[2]);
-               via_pram_command(0x89, &result.cdata[1]);
-               via_pram_command(0x8D, &result.cdata[0]);
-       } while (result.idata != last_result.idata);
-
-       return result.idata;
-}
-
-/*
- * Set the current time to a number of seconds since January 1, 1904.
- *
- * This only works on machines with the VIA-based PRAM/RTC, which
- * is basically any machine with Mac II-style ADB.
- */
-
-void via_write_time(__u32 time)
-{
-       union {
-               __u8  cdata[4];
-               __u32 idata;
-       } data;
-       __u8    temp;
-
-       /* Clear the write protect bit */
-
-       temp = 0x55;
-       via_pram_command(0x35, &temp);
-
-       data.idata = time;
-       via_pram_command(0x01, &data.cdata[3]);
-       via_pram_command(0x05, &data.cdata[2]);
-       via_pram_command(0x09, &data.cdata[1]);
-       via_pram_command(0x0D, &data.cdata[0]);
-
-       /* Set the write protect bit */
-
-       temp = 0xD5;
-       via_pram_command(0x35, &temp);
-}
index 456520a6434d89c1965c507893d1ec7d832f0498..146dc7279b2c34c8dda751bd0fdf6607bb54f045 100644 (file)
@@ -207,7 +207,7 @@ void __init paging_init(void)
 {
        int chunk;
        unsigned long mem_avail = 0;
-       unsigned int zones_size[3] = { 0, };
+       unsigned long zones_size[3] = { 0, };
 
 #ifdef DEBUG
        {
index bd59fecba710d6e8debdfc0bf305a05bf44e4822..8d6632d2f541af05e3d1153d84778b2436274fe2 100644 (file)
@@ -30,7 +30,7 @@
 
 extern void mmu_emu_init (void);
 
-extern unsigned long free_area_init(unsigned long, unsigned long);
+extern unsigned long free_area_init(unsigned long *zones_size);
 
 const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n";
 
index 1500424012982ffe950f80eb4050c150df98ecfd..88d28c2ade03b1b5e9f1449f92bf6f4152bc708a 100644 (file)
@@ -45,8 +45,12 @@ CONSOLE  =console.o
 SERIAL   =serial.o
 
 ifeq ($(ARCH),m68k)
-  KEYBD    =
-  SERIAL   =
+   ifdef CONFIG_AMIGA
+      KEYBD = amikeyb.o
+   else
+      KEYBD    =
+   endif
+   SERIAL   =
 endif
 
 ifeq ($(ARCH),arm)
@@ -60,6 +64,7 @@ ifneq ($(CONFIG_SUN_SERIAL),)
   SERIAL   =
 endif
 
+
 obj-$(CONFIG_VT) += vt.o vc_screen.o consolemap.o consolemap_deftbl.o $(CONSOLE) selection.o
 obj-$(CONFIG_SERIAL) += $(SERIAL)
 obj-$(CONFIG_SERIAL_21285) += serial_21285.o
@@ -96,6 +101,7 @@ obj-$(CONFIG_ESPSERIAL) += esp.o
 obj-$(CONFIG_SYNCLINK) += synclink.o
 obj-$(CONFIG_N_HDLC) += n_hdlc.o
 obj-$(CONFIG_SPECIALIX) += specialix.o
+obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o
 
 ifeq ($(CONFIG_SX),y)
 obj-y += sx.o generic_serial.o
index 42e1ea7c85806f2752120f04366dd4f4159327f2..41bb7ac159fdd0806a7366003e98e04b36b7bf71 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/errno.h>
 #include <linux/keyboard.h>
+#include <linux/kd.h>
+#include <linux/kbd_ll.h>
 #include <linux/delay.h>
 #include <linux/timer.h>
-#include <linux/kd.h>
 #include <linux/random.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/kbd_ll.h>
+#include <linux/kbd_kern.h>
 
 #include <asm/amigaints.h>
 #include <asm/amigahw.h>
@@ -230,7 +231,7 @@ static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp)
     /* switch CIA serial port to input mode */
     ciaa.cra &= ~0x40;
 
-    mark_bh(KEYBOARD_BH);
+    tasklet_schedule(&keyboard_tasklet);
 
     /* rotate scan code to get up/down bit in proper position */
     scancode = ((scancode >> 1) & 0x7f) | ((scancode << 7) & 0x80);
diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c
new file mode 100644 (file)
index 0000000..ea828cc
--- /dev/null
@@ -0,0 +1,2264 @@
+/*
+ *  linux/drivers/char/amiserial.c
+ *
+ * Serial driver for the amiga builtin port.
+ *
+ * This code was created by taking serial.c version 4.30 from kernel
+ * release 2.3.22, replacing all hardware related stuff with the
+ * corresponding amiga hardware actions, and removing all irrelevant
+ * code. As a consequence, it uses many of the constants and names
+ * associated with the registers and bits of 16550 compatible UARTS -
+ * but only to keep track of status, etc in the state variables. It
+ * was done this was to make it easier to keep the code in line with
+ * (non hardware specific) changes to serial.c.
+ *
+ * The port is registered with the tty driver as minor device 64, and
+ * therefore other ports should should only use 65 upwards.
+ *
+ * Richard Lucock 28/12/99
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *  Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 
+ *             1998, 1999  Theodore Ts'o
+ *
+ */
+
+/*
+ * Serial driver configuration section.  Here are the various options:
+ *
+ * SERIAL_PARANOIA_CHECK
+ *             Check the magic number for the async_structure where
+ *             ever possible.
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#undef SERIAL_PARANOIA_CHECK
+#define SERIAL_DO_RESTART
+
+/* Set of debugging defines */
+
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+
+/* Sanity checks */
+
+#define SERIAL_INLINE
+  
+#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
+#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
+ kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s)
+#else
+#define DBG_CNT(s)
+#endif
+
+/*
+ * End of serial driver configuration section.
+ */
+
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+#include <linux/serial_reg.h>
+static char *serial_version = "4.30";
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#ifdef CONFIG_AMIGA
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#endif
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+
+#ifdef SERIAL_INLINE
+#define _INLINE_ inline
+#endif
+       
+static char *serial_name = "Amiga-builtin serial driver";
+
+static DECLARE_TASK_QUEUE(tq_serial);
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#ifndef SERIAL_TYPE_NORMAL
+#define SERIAL_TYPE_NORMAL     1
+#define SERIAL_TYPE_CALLOUT    2
+#endif
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+static struct async_struct *IRQ_ports;
+
+static unsigned char current_ctl_bits;
+
+static void change_speed(struct async_struct *info, struct termios *old);
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
+
+
+static struct serial_state rs_table[1];
+
+#define NR_PORTS       (sizeof(rs_table)/sizeof(struct serial_state))
+
+
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+#ifndef MIN
+#define MIN(a,b)       ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the copy_from_user blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf;
+static DECLARE_MUTEX(tmp_buf_sem);
+
+#include <asm/uaccess.h>
+
+#define serial_isroot()        (capable(CAP_SYS_ADMIN))
+
+
+static inline int serial_paranoia_check(struct async_struct *info,
+                                       kdev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+       static const char *badmagic =
+               "Warning: bad magic number for serial struct (%s) in %s\n";
+       static const char *badinfo =
+               "Warning: null async_struct for (%s) in %s\n";
+
+       if (!info) {
+               printk(badinfo, kdevname(device), routine);
+               return 1;
+       }
+       if (info->magic != SERIAL_MAGIC) {
+               printk(badmagic, kdevname(device), routine);
+               return 1;
+       }
+#endif
+       return 0;
+}
+
+/* some serial hardware definitions */
+#define SDR_OVRUN   (1<<15)
+#define SDR_RBF     (1<<14)
+#define SDR_TBE     (1<<13)
+#define SDR_TSRE    (1<<12)
+
+#define SERPER_PARENB    (1<<15)
+
+#define AC_SETCLR   (1<<15)
+#define AC_UARTBRK  (1<<11)
+
+#define SER_DTR     (1<<7)
+#define SER_RTS     (1<<6)
+#define SER_DCD     (1<<5)
+#define SER_CTS     (1<<4)
+#define SER_DSR     (1<<3)
+
+static __inline__ void rtsdtr_ctrl(int bits)
+{
+    ciab.pra = ((bits & (SER_RTS | SER_DTR)) ^ (SER_RTS | SER_DTR)) | (ciab.pra & ~(SER_RTS | SER_DTR));
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->device, "rs_stop"))
+               return;
+       
+       save_flags(flags); cli();
+       if (info->IER & UART_IER_THRI) {
+               info->IER &= ~UART_IER_THRI;
+               /* disable Tx interrupt and remove any pending interrupts */
+               custom.intena = IF_TBE;
+               mb();
+               custom.intreq = IF_TBE;
+               mb();
+       }
+       restore_flags(flags);
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+       unsigned long flags;
+       
+       if (serial_paranoia_check(info, tty->device, "rs_start"))
+               return;
+       
+       save_flags(flags); cli();
+       if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) {
+               info->IER |= UART_IER_THRI;
+               custom.intena = IF_SETCLR | IF_TBE;
+               mb();
+               /* set a pending Tx Interrupt, transmitter should restart now */
+               custom.intreq = IF_SETCLR | IF_TBE;
+               mb();
+       }
+       restore_flags(flags);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt().  They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off.  People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible.  After you are done making modifications, it is not a bad
+ * idea to do:
+ * 
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ *                             - Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void rs_sched_event(struct async_struct *info,
+                                 int event)
+{
+       info->event |= 1 << event;
+       queue_task(&info->tqueue, &tq_serial);
+       mark_bh(SERIAL_BH);
+}
+
+static _INLINE_ void receive_chars(struct async_struct *info)
+{
+        int status;
+       int serdatr;
+       struct tty_struct *tty = info->tty;
+       unsigned char ch;
+       struct  async_icount *icount;
+
+       icount = &info->state->icount;
+
+       status = UART_LSR_DR; /* We obviously have a character! */
+       serdatr = custom.serdatr;
+       mb();
+       custom.intreq = IF_RBF;
+       mb();
+
+       if((serdatr & 0x1ff) == 0)
+           status |= UART_LSR_BI;
+       if(serdatr & SDR_OVRUN)
+           status |= UART_LSR_OE;
+                 
+       ch = serdatr & 0xff;
+       if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+         goto ignore_char;
+       *tty->flip.char_buf_ptr = ch;
+       icount->rx++;
+               
+#ifdef SERIAL_DEBUG_INTR
+       printk("DR%02x:%02x...", ch, status);
+#endif
+       *tty->flip.flag_buf_ptr = 0;
+
+       /*
+        * We don't handle parity or frame errors - but I have left
+        * the code in, since I'm not sure that the errors can't be
+        * detected.
+        */
+
+       if (status & (UART_LSR_BI | UART_LSR_PE |
+                     UART_LSR_FE | UART_LSR_OE)) {
+         /*
+          * For statistics only
+          */
+         if (status & UART_LSR_BI) {
+           status &= ~(UART_LSR_FE | UART_LSR_PE);
+           icount->brk++;
+         } else if (status & UART_LSR_PE)
+           icount->parity++;
+         else if (status & UART_LSR_FE)
+           icount->frame++;
+         if (status & UART_LSR_OE)
+           icount->overrun++;
+
+         /*
+          * Now check to see if character should be
+          * ignored, and mask off conditions which
+          * should be ignored.
+          */
+         if (status & info->ignore_status_mask)
+           goto ignore_char;
+
+         status &= info->read_status_mask;
+
+         if (status & (UART_LSR_BI)) {
+#ifdef SERIAL_DEBUG_INTR
+           printk("handling break....");
+#endif
+           *tty->flip.flag_buf_ptr = TTY_BREAK;
+           if (info->flags & ASYNC_SAK)
+             do_SAK(tty);
+         } else if (status & UART_LSR_PE)
+           *tty->flip.flag_buf_ptr = TTY_PARITY;
+         else if (status & UART_LSR_FE)
+           *tty->flip.flag_buf_ptr = TTY_FRAME;
+         if (status & UART_LSR_OE) {
+           /*
+            * Overrun is special, since it's
+            * reported immediately, and doesn't
+            * affect the current character
+            */
+           if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+             tty->flip.count++;
+             tty->flip.flag_buf_ptr++;
+             tty->flip.char_buf_ptr++;
+             *tty->flip.flag_buf_ptr = TTY_OVERRUN;
+           }
+         }
+       }
+       tty->flip.flag_buf_ptr++;
+       tty->flip.char_buf_ptr++;
+       tty->flip.count++;
+ ignore_char:
+
+       tty_flip_buffer_push(tty);
+}
+
+static _INLINE_ void transmit_chars(struct async_struct *info)
+{
+       custom.intreq = IF_TBE;
+       mb();
+       if (info->x_char) {
+               custom.serdat = info->x_char | 0x100;
+               mb();
+               info->state->icount.tx++;
+               info->x_char = 0;
+               return;
+       }
+       if ((info->xmit_cnt <= 0) || info->tty->stopped ||
+           info->tty->hw_stopped) {
+               info->IER &= ~UART_IER_THRI;
+               custom.intena = IF_TBE;
+               mb();
+               return;
+       }
+       
+       custom.serdat = info->xmit_buf[info->xmit_tail++] | 0x100;
+       mb();
+       info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+       info->state->icount.tx++;
+       --info->xmit_cnt;
+       
+       if (info->xmit_cnt < WAKEUP_CHARS)
+               rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+#ifdef SERIAL_DEBUG_INTR
+       printk("THRE...");
+#endif
+       if (info->xmit_cnt <= 0) {
+               custom.intena = IF_TBE;
+               mb();
+               info->IER &= ~UART_IER_THRI;
+       }
+}
+
+static _INLINE_ void check_modem_status(struct async_struct *info)
+{
+       unsigned char status = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);
+       unsigned char dstatus;
+       struct  async_icount *icount;
+
+       /* Determine bits that have changed */
+       dstatus = status ^ current_ctl_bits;
+       current_ctl_bits = status;
+
+       if (dstatus) {
+               icount = &info->state->icount;
+               /* update input line counters */
+               if (dstatus & SER_DSR)
+                       icount->dsr++;
+               if (dstatus & SER_DCD) {
+                       icount->dcd++;
+#ifdef CONFIG_HARD_PPS
+                       if ((info->flags & ASYNC_HARDPPS_CD) &&
+                           !(status & SER_DCD))
+                               hardpps();
+#endif
+               }
+               if (dstatus & SER_CTS)
+                       icount->cts++;
+               wake_up_interruptible(&info->delta_msr_wait);
+       }
+
+       if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) {
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+               printk("ttyS%02d CD now %s...", info->line,
+                      (!(status & SER_DCD)) ? "on" : "off");
+#endif         
+               if (!(status & SER_DCD))
+                       wake_up_interruptible(&info->open_wait);
+               else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+                          (info->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SERIAL_DEBUG_OPEN
+                       printk("doing serial hangup...");
+#endif
+                       if (info->tty)
+                               tty_hangup(info->tty);
+               }
+       }
+       if (info->flags & ASYNC_CTS_FLOW) {
+               if (info->tty->hw_stopped) {
+                       if (!(status & SER_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+                               printk("CTS tx start...");
+#endif
+                               info->tty->hw_stopped = 0;
+                               info->IER |= UART_IER_THRI;
+                               custom.intena = IF_SETCLR | IF_TBE;
+                               mb();
+                               /* set a pending Tx Interrupt, transmitter should restart now */
+                               custom.intreq = IF_SETCLR | IF_TBE;
+                               mb();
+                               rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+                               return;
+                       }
+               } else {
+                       if ((status & SER_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+                               printk("CTS tx stop...");
+#endif
+                               info->tty->hw_stopped = 1;
+                               info->IER &= ~UART_IER_THRI;
+                               /* disable Tx interrupt and remove any pending interrupts */
+                               custom.intena = IF_TBE;
+                               mb();
+                               custom.intreq = IF_TBE;
+                               mb();
+                       }
+               }
+       }
+}
+
+static void ser_vbl_int( int irq, void *data, struct pt_regs *regs)
+{
+        /* vbl is just a periodic interrupt we tie into to update modem status */
+       struct async_struct * info = IRQ_ports;
+       /*
+        * TBD - is it better to unregister from this interrupt or to
+        * ignore it if MSI is clear ?
+        */
+       if(info->IER & UART_IER_MSI)
+         check_modem_status(info);
+}
+
+static void ser_rx_int(int irq, void *dev_id, struct pt_regs * regs)
+{
+       struct async_struct * info;
+       
+#ifdef SERIAL_DEBUG_INTR
+       printk("ser_rx_int...");
+#endif
+       
+       info = IRQ_ports;
+       if (!info || !info->tty)
+               return;
+
+       receive_chars(info);
+       info->last_active = jiffies;
+#ifdef SERIAL_DEBUG_INTR
+       printk("end.\n");
+#endif
+}
+
+static void ser_tx_int(int irq, void *dev_id, struct pt_regs * regs)
+{
+       struct async_struct * info;
+       
+       if (custom.serdatr & SDR_TBE) {
+#ifdef SERIAL_DEBUG_INTR
+         printk("ser_tx_int...");
+#endif
+       
+         info = IRQ_ports;
+         if (!info || !info->tty)
+           return;
+
+         transmit_chars(info);
+         info->last_active = jiffies;
+#ifdef SERIAL_DEBUG_INTR
+         printk("end.\n");
+#endif
+       }
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+       run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+       struct async_struct     *info = (struct async_struct *) private_;
+       struct tty_struct       *tty;
+       
+       tty = info->tty;
+       if (!tty)
+               return;
+
+       if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+               if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+                   tty->ldisc.write_wakeup)
+                       (tty->ldisc.write_wakeup)(tty);
+               wake_up_interruptible(&tty->write_wait);
+       }
+}
+
+/*
+ * ---------------------------------------------------------------
+ * Low level utility subroutines for the serial driver:  routines to
+ * figure out the appropriate timeout for an interrupt chain, routines
+ * to initialize and startup a serial port, and routines to shutdown a
+ * serial port.  Useful stuff like that.
+ * ---------------------------------------------------------------
+ */
+
+static int startup(struct async_struct * info)
+{
+       unsigned long flags;
+       int     retval=0;
+       unsigned long page;
+
+       page = get_free_page(GFP_KERNEL);
+       if (!page)
+               return -ENOMEM;
+
+       save_flags(flags); cli();
+
+       if (info->flags & ASYNC_INITIALIZED) {
+               free_page(page);
+               goto errout;
+       }
+
+       if (info->xmit_buf)
+               free_page(page);
+       else
+               info->xmit_buf = (unsigned char *) page;
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("starting up ttys%d ...", info->line);
+#endif
+
+       /* Clear anything in the input buffer */
+
+       custom.intreq = IF_RBF;
+       mb();
+
+       retval = request_irq(IRQ_AMIGA_VERTB, ser_vbl_int, 0, "serial status", info);
+       if (retval) {
+         if (serial_isroot()) {
+           if (info->tty)
+             set_bit(TTY_IO_ERROR,
+                     &info->tty->flags);
+           retval = 0;
+         }
+         goto errout;
+       }
+
+       /* enable both Rx and Tx interrupts */
+       custom.intena = IF_SETCLR | IF_RBF | IF_TBE;
+       mb();
+       info->IER = UART_IER_MSI;
+
+       /* remember current state of the DCD and CTS bits */
+       current_ctl_bits = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);
+
+       IRQ_ports = info;
+
+       info->MCR = 0;
+       if (info->tty->termios->c_cflag & CBAUD)
+         info->MCR = SER_DTR | SER_RTS;
+       rtsdtr_ctrl(info->MCR);
+
+       if (info->tty)
+               clear_bit(TTY_IO_ERROR, &info->tty->flags);
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+       /*
+        * Set up the tty->alt_speed kludge
+        */
+       if (info->tty) {
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                       info->tty->alt_speed = 57600;
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                       info->tty->alt_speed = 115200;
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                       info->tty->alt_speed = 230400;
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                       info->tty->alt_speed = 460800;
+       }
+       
+       /*
+        * and set the speed of the serial port
+        */
+       change_speed(info, 0);
+
+       info->flags |= ASYNC_INITIALIZED;
+       restore_flags(flags);
+       return 0;
+       
+errout:
+       restore_flags(flags);
+       return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct async_struct * info)
+{
+       unsigned long   flags;
+       struct serial_state *state;
+
+       if (!(info->flags & ASYNC_INITIALIZED))
+               return;
+
+       state = info->state;
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("Shutting down serial port %d ....\n", info->line);
+#endif
+       
+       save_flags(flags); cli(); /* Disable interrupts */
+
+       /*
+        * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+        * here so the queue might never be waken up
+        */
+       wake_up_interruptible(&info->delta_msr_wait);
+       
+       IRQ_ports = NULL;
+       
+       /*
+        * Free the IRQ, if necessary
+        */
+       free_irq(IRQ_AMIGA_VERTB, info);
+
+       if (info->xmit_buf) {
+               free_page((unsigned long) info->xmit_buf);
+               info->xmit_buf = 0;
+       }
+
+       info->IER = 0;
+       custom.intena = IF_RBF | IF_TBE;
+       mb();
+
+       /* disable break condition */
+       custom.adkcon = AC_UARTBRK;
+       mb();
+       
+       if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
+               info->MCR &= ~(SER_DTR|SER_RTS);
+       rtsdtr_ctrl(info->MCR);
+
+       if (info->tty)
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       info->flags &= ~ASYNC_INITIALIZED;
+       restore_flags(flags);
+}
+
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct async_struct *info,
+                        struct termios *old_termios)
+{
+       int     quot = 0, baud_base, baud;
+       unsigned cflag, cval = 0;
+       int     bits;
+       unsigned long   flags;
+
+       if (!info->tty || !info->tty->termios)
+               return;
+       cflag = info->tty->termios->c_cflag;
+
+       /* Byte size is always 8 bits plus parity bit if requested */
+
+       cval = 3; bits = 10;
+       if (cflag & CSTOPB) {
+               cval |= 0x04;
+               bits++;
+       }
+       if (cflag & PARENB) {
+               cval |= UART_LCR_PARITY;
+               bits++;
+       }
+       if (!(cflag & PARODD))
+               cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+       if (cflag & CMSPAR)
+               cval |= UART_LCR_SPAR;
+#endif
+
+       /* Determine divisor based on baud rate */
+       baud = tty_get_baud_rate(info->tty);
+       if (!baud)
+               baud = 9600;    /* B0 transition handled in rs_set_termios */
+       baud_base = info->state->baud_base;
+       if (baud == 38400 &&
+           ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+               quot = info->state->custom_divisor;
+       else {
+               if (baud == 134)
+                       /* Special case since 134 is really 134.5 */
+                       quot = (2*baud_base / 269);
+               else if (baud)
+                       quot = baud_base / baud;
+       }
+       /* If the quotient is zero refuse the change */
+       if (!quot && old_termios) {
+               info->tty->termios->c_cflag &= ~CBAUD;
+               info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
+               baud = tty_get_baud_rate(info->tty);
+               if (!baud)
+                       baud = 9600;
+               if (baud == 38400 &&
+                   ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+                       quot = info->state->custom_divisor;
+               else {
+                       if (baud == 134)
+                               /* Special case since 134 is really 134.5 */
+                               quot = (2*baud_base / 269);
+                       else if (baud)
+                               quot = baud_base / baud;
+               }
+       }
+       /* As a last resort, if the quotient is zero, default to 9600 bps */
+       if (!quot)
+               quot = baud_base / 9600;
+       info->quot = quot;
+       info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
+       info->timeout += HZ/50;         /* Add .02 seconds of slop */
+
+       /* CTS flow control flag and modem status interrupts */
+       info->IER &= ~UART_IER_MSI;
+       if (info->flags & ASYNC_HARDPPS_CD)
+               info->IER |= UART_IER_MSI;
+       if (cflag & CRTSCTS) {
+               info->flags |= ASYNC_CTS_FLOW;
+               info->IER |= UART_IER_MSI;
+       } else
+               info->flags &= ~ASYNC_CTS_FLOW;
+       if (cflag & CLOCAL)
+               info->flags &= ~ASYNC_CHECK_CD;
+       else {
+               info->flags |= ASYNC_CHECK_CD;
+               info->IER |= UART_IER_MSI;
+       }
+       /* TBD:
+        * Does clearing IER_MSI imply that we should disbale the VBL interrupt ?
+        */
+
+       /*
+        * Set up parity check flag
+        */
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+       info->read_status_mask = UART_LSR_OE | UART_LSR_DR;
+       if (I_INPCK(info->tty))
+               info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+       if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+               info->read_status_mask |= UART_LSR_BI;
+       
+       /*
+        * Characters to ignore
+        */
+       info->ignore_status_mask = 0;
+       if (I_IGNPAR(info->tty))
+               info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+       if (I_IGNBRK(info->tty)) {
+               info->ignore_status_mask |= UART_LSR_BI;
+               /*
+                * If we're ignore parity and break indicators, ignore 
+                * overruns too.  (For real raw support).
+                */
+               if (I_IGNPAR(info->tty))
+                       info->ignore_status_mask |= UART_LSR_OE;
+       }
+       /*
+        * !!! ignore all characters if CREAD is not set
+        */
+       if ((cflag & CREAD) == 0)
+               info->ignore_status_mask |= UART_LSR_DR;
+       save_flags(flags); cli();
+
+       {
+         short serper;
+
+       /* Set up the baud rate */
+         serper = quot - 1;
+
+       /* Enable or disable parity bit */
+
+       if(cval & UART_LCR_PARITY)
+         serper |= (SERPER_PARENB);
+
+       custom.serper = serper;
+       mb();
+       }
+
+       info->LCR = cval;                               /* Save LCR */
+       restore_flags(flags);
+}
+
+static void rs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->device, "rs_put_char"))
+               return;
+
+       if (!tty || !info->xmit_buf)
+               return;
+
+       save_flags(flags); cli();
+       if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+               restore_flags(flags);
+               return;
+       }
+
+       info->xmit_buf[info->xmit_head++] = ch;
+       info->xmit_head &= SERIAL_XMIT_SIZE-1;
+       info->xmit_cnt++;
+       restore_flags(flags);
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+       unsigned long flags;
+                               
+       if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
+               return;
+
+       if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+           !info->xmit_buf)
+               return;
+
+       save_flags(flags); cli();
+       info->IER |= UART_IER_THRI;
+       custom.intena = IF_SETCLR | IF_TBE;
+       mb();
+       /* set a pending Tx Interrupt, transmitter should restart now */
+       custom.intreq = IF_SETCLR | IF_TBE;
+       mb();
+       restore_flags(flags);
+}
+
+static int rs_write(struct tty_struct * tty, int from_user,
+                   const unsigned char *buf, int count)
+{
+       int     c, ret = 0;
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->device, "rs_write"))
+               return 0;
+
+       if (!tty || !info->xmit_buf || !tmp_buf)
+               return 0;
+
+       save_flags(flags);
+       if (from_user) {
+               down(&tmp_buf_sem);
+               while (1) {
+                       c = MIN(count,
+                               MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                                   SERIAL_XMIT_SIZE - info->xmit_head));
+                       if (c <= 0)
+                               break;
+
+                       c -= copy_from_user(tmp_buf, buf, c);
+                       if (!c) {
+                               if (!ret)
+                                       ret = -EFAULT;
+                               break;
+                       }
+                       cli();
+                       c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                                      SERIAL_XMIT_SIZE - info->xmit_head));
+                       memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+                       info->xmit_head = ((info->xmit_head + c) &
+                                          (SERIAL_XMIT_SIZE-1));
+                       info->xmit_cnt += c;
+                       restore_flags(flags);
+                       buf += c;
+                       count -= c;
+                       ret += c;
+               }
+               up(&tmp_buf_sem);
+       } else {
+               while (1) {
+                       cli();          
+                       c = MIN(count,
+                               MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                                   SERIAL_XMIT_SIZE - info->xmit_head));
+                       if (c <= 0) {
+                               restore_flags(flags);
+                               break;
+                       }
+                       memcpy(info->xmit_buf + info->xmit_head, buf, c);
+                       info->xmit_head = ((info->xmit_head + c) &
+                                          (SERIAL_XMIT_SIZE-1));
+                       info->xmit_cnt += c;
+                       restore_flags(flags);
+                       buf += c;
+                       count -= c;
+                       ret += c;
+               }
+       }
+       if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+           !(info->IER & UART_IER_THRI)) {
+               info->IER |= UART_IER_THRI;
+               cli();
+               custom.intena = IF_SETCLR | IF_TBE;
+               mb();
+               /* set a pending Tx Interrupt, transmitter should restart now */
+               custom.intreq = IF_SETCLR | IF_TBE;
+               mb();
+               restore_flags(flags);
+       }
+       return ret;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+       int     ret;
+                               
+       if (serial_paranoia_check(info, tty->device, "rs_write_room"))
+               return 0;
+       ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+       if (ret < 0)
+               ret = 0;
+       return ret;
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+                               
+       if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
+               return 0;
+       return info->xmit_cnt;
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+       unsigned long flags;
+       
+       if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
+               return;
+       save_flags(flags); cli();
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       restore_flags(flags);
+       wake_up_interruptible(&tty->write_wait);
+       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+           tty->ldisc.write_wakeup)
+               (tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void rs_send_xchar(struct tty_struct *tty, char ch)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+        unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->device, "rs_send_char"))
+               return;
+
+       info->x_char = ch;
+       if (ch) {
+               /* Make sure transmit interrupts are on */
+
+               /* Check this ! */
+               save_flags(flags);
+               cli();
+               if(!(custom.intenar & IF_TBE)) {
+                   custom.intena = IF_SETCLR | IF_TBE;
+                   mb();
+                   /* set a pending Tx Interrupt, transmitter should restart now */
+                   custom.intreq = IF_SETCLR | IF_TBE;
+                   mb();
+               }
+               restore_flags(flags);
+
+               info->IER |= UART_IER_THRI;
+       }
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ * 
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+       unsigned long flags;
+#ifdef SERIAL_DEBUG_THROTTLE
+       char    buf[64];
+       
+       printk("throttle %s: %d....\n", tty_name(tty, buf),
+              tty->ldisc.chars_in_buffer(tty));
+#endif
+
+       if (serial_paranoia_check(info, tty->device, "rs_throttle"))
+               return;
+       
+       if (I_IXOFF(tty))
+               rs_send_xchar(tty, STOP_CHAR(tty));
+
+       if (tty->termios->c_cflag & CRTSCTS)
+               info->MCR &= ~SER_RTS;
+
+       save_flags(flags); cli();
+       rtsdtr_ctrl(info->MCR);
+       restore_flags(flags);
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+       unsigned long flags;
+#ifdef SERIAL_DEBUG_THROTTLE
+       char    buf[64];
+       
+       printk("unthrottle %s: %d....\n", tty_name(tty, buf),
+              tty->ldisc.chars_in_buffer(tty));
+#endif
+
+       if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
+               return;
+       
+       if (I_IXOFF(tty)) {
+               if (info->x_char)
+                       info->x_char = 0;
+               else
+                       rs_send_xchar(tty, START_CHAR(tty));
+       }
+       if (tty->termios->c_cflag & CRTSCTS)
+               info->MCR |= SER_RTS;
+       save_flags(flags); cli();
+       rtsdtr_ctrl(info->MCR);
+       restore_flags(flags);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct async_struct * info,
+                          struct serial_struct * retinfo)
+{
+       struct serial_struct tmp;
+       struct serial_state *state = info->state;
+   
+       if (!retinfo)
+               return -EFAULT;
+       memset(&tmp, 0, sizeof(tmp));
+       tmp.type = state->type;
+       tmp.line = state->line;
+       tmp.port = state->port;
+       tmp.irq = state->irq;
+       tmp.flags = state->flags;
+       tmp.xmit_fifo_size = state->xmit_fifo_size;
+       tmp.baud_base = state->baud_base;
+       tmp.close_delay = state->close_delay;
+       tmp.closing_wait = state->closing_wait;
+       tmp.custom_divisor = state->custom_divisor;
+       if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+               return -EFAULT;
+       return 0;
+}
+
+static int set_serial_info(struct async_struct * info,
+                          struct serial_struct * new_info)
+{
+       struct serial_struct new_serial;
+       struct serial_state old_state, *state;
+       unsigned int            change_irq,change_port;
+       int                     retval = 0;
+
+       if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+               return -EFAULT;
+       state = info->state;
+       old_state = *state;
+  
+       change_irq = new_serial.irq != state->irq;
+       change_port = (new_serial.port != state->port);
+       if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size))
+         return -EINVAL;
+  
+       if (!serial_isroot()) {
+               if ((new_serial.baud_base != state->baud_base) ||
+                   (new_serial.close_delay != state->close_delay) ||
+                   (new_serial.xmit_fifo_size != state->xmit_fifo_size) ||
+                   ((new_serial.flags & ~ASYNC_USR_MASK) !=
+                    (state->flags & ~ASYNC_USR_MASK)))
+                       return -EPERM;
+               state->flags = ((state->flags & ~ASYNC_USR_MASK) |
+                              (new_serial.flags & ASYNC_USR_MASK));
+               info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+                              (new_serial.flags & ASYNC_USR_MASK));
+               state->custom_divisor = new_serial.custom_divisor;
+               goto check_and_exit;
+       }
+
+       if (new_serial.baud_base < 9600)
+               return -EINVAL;
+
+       /*
+        * OK, past this point, all the error checking has been done.
+        * At this point, we start making changes.....
+        */
+
+       state->baud_base = new_serial.baud_base;
+       state->flags = ((state->flags & ~ASYNC_FLAGS) |
+                       (new_serial.flags & ASYNC_FLAGS));
+       info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) |
+                      (info->flags & ASYNC_INTERNAL_FLAGS));
+       state->custom_divisor = new_serial.custom_divisor;
+       state->close_delay = new_serial.close_delay * HZ/100;
+       state->closing_wait = new_serial.closing_wait * HZ/100;
+       info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+       
+check_and_exit:
+       if (info->flags & ASYNC_INITIALIZED) {
+               if (((old_state.flags & ASYNC_SPD_MASK) !=
+                    (state->flags & ASYNC_SPD_MASK)) ||
+                   (old_state.custom_divisor != state->custom_divisor)) {
+                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                               info->tty->alt_speed = 57600;
+                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                               info->tty->alt_speed = 115200;
+                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                               info->tty->alt_speed = 230400;
+                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                               info->tty->alt_speed = 460800;
+                       change_speed(info, 0);
+               }
+       } else
+               retval = startup(info);
+       return retval;
+}
+
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ *         is emptied.  On bus types like RS485, the transmitter must
+ *         release the bus after transmitting. This must be done when
+ *         the transmit shift register is empty, not be done when the
+ *         transmit holding register is empty.  This functionality
+ *         allows an RS485 driver to be written in user space. 
+ */
+static int get_lsr_info(struct async_struct * info, unsigned int *value)
+{
+       unsigned char status;
+       unsigned int result;
+       unsigned long flags;
+
+       save_flags(flags); cli();
+       status = custom.serdatr;
+       mb();
+       restore_flags(flags);
+       result = ((status & SDR_TSRE) ? TIOCSER_TEMT : 0);
+       if (copy_to_user(value, &result, sizeof(int)))
+               return -EFAULT;
+       return 0;
+}
+
+
+static int get_modem_info(struct async_struct * info, unsigned int *value)
+{
+       unsigned char control, status;
+       unsigned int result;
+       unsigned long flags;
+
+       control = info->MCR;
+       save_flags(flags); cli();
+       status = ciab.pra;
+       restore_flags(flags);
+       result =  ((control & SER_RTS) ? TIOCM_RTS : 0)
+               | ((control & SER_DTR) ? TIOCM_DTR : 0)
+               | (!(status  & SER_DCD) ? TIOCM_CAR : 0)
+               | (!(status  & SER_DSR) ? TIOCM_DSR : 0)
+               | (!(status  & SER_CTS) ? TIOCM_CTS : 0);
+       if (copy_to_user(value, &result, sizeof(int)))
+               return -EFAULT;
+       return 0;
+}
+
+static int set_modem_info(struct async_struct * info, unsigned int cmd,
+                         unsigned int *value)
+{
+       unsigned int arg;
+       unsigned long flags;
+
+       if (copy_from_user(&arg, value, sizeof(int)))
+               return -EFAULT;
+
+       switch (cmd) {
+       case TIOCMBIS: 
+               if (arg & TIOCM_RTS)
+                       info->MCR |= SER_RTS;
+               if (arg & TIOCM_DTR)
+                       info->MCR |= SER_DTR;
+               break;
+       case TIOCMBIC:
+               if (arg & TIOCM_RTS)
+                       info->MCR &= ~SER_RTS;
+               if (arg & TIOCM_DTR)
+                       info->MCR &= ~SER_DTR;
+               break;
+       case TIOCMSET:
+               info->MCR = ((info->MCR & ~(SER_RTS | SER_DTR))
+                            | ((arg & TIOCM_RTS) ? SER_RTS : 0)
+                            | ((arg & TIOCM_DTR) ? SER_DTR : 0));
+               break;
+       default:
+               return -EINVAL;
+       }
+       save_flags(flags); cli();
+       rtsdtr_ctrl(info->MCR);
+       restore_flags(flags);
+       return 0;
+}
+
+/*
+ * rs_break() --- routine which turns the break handling on or off
+ */
+static void rs_break(struct tty_struct *tty, int break_state)
+{
+       struct async_struct * info = (struct async_struct *)tty->driver_data;
+       unsigned long flags;
+       
+       if (serial_paranoia_check(info, tty->device, "rs_break"))
+               return;
+
+       save_flags(flags); cli();
+       if (break_state == -1)
+         custom.adkcon = AC_SETCLR | AC_UARTBRK;
+       else
+         custom.adkcon = AC_UARTBRK;
+       mb();
+       restore_flags(flags);
+}
+
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+                   unsigned int cmd, unsigned long arg)
+{
+       struct async_struct * info = (struct async_struct *)tty->driver_data;
+       struct async_icount cprev, cnow;        /* kernel counter temps */
+       struct serial_icounter_struct icount;
+       unsigned long flags;
+       
+       if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
+               return -ENODEV;
+
+       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+           (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
+           (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+               if (tty->flags & (1 << TTY_IO_ERROR))
+                   return -EIO;
+       }
+       
+       switch (cmd) {
+               case TIOCMGET:
+                       return get_modem_info(info, (unsigned int *) arg);
+               case TIOCMBIS:
+               case TIOCMBIC:
+               case TIOCMSET:
+                       return set_modem_info(info, cmd, (unsigned int *) arg);
+               case TIOCGSERIAL:
+                       return get_serial_info(info,
+                                              (struct serial_struct *) arg);
+               case TIOCSSERIAL:
+                       return set_serial_info(info,
+                                              (struct serial_struct *) arg);
+               case TIOCSERCONFIG:
+                       return 0;
+
+               case TIOCSERGETLSR: /* Get line status register */
+                       return get_lsr_info(info, (unsigned int *) arg);
+
+               case TIOCSERGSTRUCT:
+                       if (copy_to_user((struct async_struct *) arg,
+                                        info, sizeof(struct async_struct)))
+                               return -EFAULT;
+                       return 0;
+                               
+               /*
+                * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+                * - mask passed in arg for lines of interest
+                *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+                * Caller should use TIOCGICOUNT to see which one it was
+                */
+               case TIOCMIWAIT:
+                       save_flags(flags); cli();
+                       /* note the counters on entry */
+                       cprev = info->state->icount;
+                       restore_flags(flags);
+                       while (1) {
+                               interruptible_sleep_on(&info->delta_msr_wait);
+                               /* see if a signal did it */
+                               if (signal_pending(current))
+                                       return -ERESTARTSYS;
+                               save_flags(flags); cli();
+                               cnow = info->state->icount; /* atomic copy */
+                               restore_flags(flags);
+                               if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
+                                   cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+                                       return -EIO; /* no change => error */
+                               if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+                                    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+                                    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+                                    ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
+                                       return 0;
+                               }
+                               cprev = cnow;
+                       }
+                       /* NOTREACHED */
+
+               /* 
+                * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+                * Return: write counters to the user passed counter struct
+                * NB: both 1->0 and 0->1 transitions are counted except for
+                *     RI where only 0->1 is counted.
+                */
+               case TIOCGICOUNT:
+                       save_flags(flags); cli();
+                       cnow = info->state->icount;
+                       restore_flags(flags);
+                       icount.cts = cnow.cts;
+                       icount.dsr = cnow.dsr;
+                       icount.rng = cnow.rng;
+                       icount.dcd = cnow.dcd;
+                       icount.rx = cnow.rx;
+                       icount.tx = cnow.tx;
+                       icount.frame = cnow.frame;
+                       icount.overrun = cnow.overrun;
+                       icount.parity = cnow.parity;
+                       icount.brk = cnow.brk;
+                       icount.buf_overrun = cnow.buf_overrun;
+                       
+                       if (copy_to_user((void *)arg, &icount, sizeof(icount)))
+                               return -EFAULT;
+                       return 0;
+               case TIOCSERGWILD:
+               case TIOCSERSWILD:
+                       /* "setserial -W" is called in Debian boot */
+                       printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
+                       return 0;
+
+               default:
+                       return -ENOIOCTLCMD;
+               }
+       return 0;
+}
+
+static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+       struct async_struct *info = (struct async_struct *)tty->driver_data;
+       unsigned long flags;
+       unsigned int cflag = tty->termios->c_cflag;
+       
+       if (   (cflag == old_termios->c_cflag)
+           && (   RELEVANT_IFLAG(tty->termios->c_iflag) 
+               == RELEVANT_IFLAG(old_termios->c_iflag)))
+         return;
+
+       change_speed(info, old_termios);
+
+       /* Handle transition to B0 status */
+       if ((old_termios->c_cflag & CBAUD) &&
+           !(cflag & CBAUD)) {
+               info->MCR &= ~(SER_DTR|SER_RTS);
+               save_flags(flags); cli();
+               rtsdtr_ctrl(info->MCR);
+               restore_flags(flags);
+       }
+       
+       /* Handle transition away from B0 status */
+       if (!(old_termios->c_cflag & CBAUD) &&
+           (cflag & CBAUD)) {
+               info->MCR |= SER_DTR;
+               if (!(tty->termios->c_cflag & CRTSCTS) || 
+                   !test_bit(TTY_THROTTLED, &tty->flags)) {
+                       info->MCR |= SER_RTS;
+               }
+               save_flags(flags); cli();
+               rtsdtr_ctrl(info->MCR);
+               restore_flags(flags);
+       }
+       
+       /* Handle turning off CRTSCTS */
+       if ((old_termios->c_cflag & CRTSCTS) &&
+           !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+               rs_start(tty);
+       }
+
+#if 0
+       /*
+        * No need to wake up processes in open wait, since they
+        * sample the CLOCAL flag once, and don't recheck it.
+        * XXX  It's not clear whether the current behavior is correct
+        * or not.  Hence, this may change.....
+        */
+       if (!(old_termios->c_cflag & CLOCAL) &&
+           (tty->termios->c_cflag & CLOCAL))
+               wake_up_interruptible(&info->open_wait);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ * 
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+       struct async_struct * info = (struct async_struct *)tty->driver_data;
+       struct serial_state *state;
+       unsigned long flags;
+
+       if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
+               return;
+
+       state = info->state;
+       
+       save_flags(flags); cli();
+       
+       if (tty_hung_up_p(filp)) {
+               DBG_CNT("before DEC-hung");
+               MOD_DEC_USE_COUNT;
+               restore_flags(flags);
+               return;
+       }
+       
+#ifdef SERIAL_DEBUG_OPEN
+       printk("rs_close ttys%d, count = %d\n", info->line, state->count);
+#endif
+       if ((tty->count == 1) && (state->count != 1)) {
+               /*
+                * Uh, oh.  tty->count is 1, which means that the tty
+                * structure will be freed.  state->count should always
+                * be one in these conditions.  If it's greater than
+                * one, we've got real problems, since it means the
+                * serial port won't be shutdown.
+                */
+               printk("rs_close: bad serial port count; tty->count is 1, "
+                      "state->count is %d\n", state->count);
+               state->count = 1;
+       }
+       if (--state->count < 0) {
+               printk("rs_close: bad serial port count for ttys%d: %d\n",
+                      info->line, state->count);
+               state->count = 0;
+       }
+       if (state->count) {
+               DBG_CNT("before DEC-2");
+               MOD_DEC_USE_COUNT;
+               restore_flags(flags);
+               return;
+       }
+       info->flags |= ASYNC_CLOSING;
+       /*
+        * Save the termios structure, since this port may have
+        * separate termios for callout and dialin.
+        */
+       if (info->flags & ASYNC_NORMAL_ACTIVE)
+               info->state->normal_termios = *tty->termios;
+       if (info->flags & ASYNC_CALLOUT_ACTIVE)
+               info->state->callout_termios = *tty->termios;
+       /*
+        * Now we wait for the transmit buffer to clear; and we notify 
+        * the line discipline to only process XON/XOFF characters.
+        */
+       tty->closing = 1;
+       if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+               tty_wait_until_sent(tty, info->closing_wait);
+       /*
+        * At this point we stop accepting input.  To do this, we
+        * disable the receive line status interrupts, and tell the
+        * interrupt driver to stop checking the data ready bit in the
+        * line status register.
+        */
+       info->read_status_mask &= ~UART_LSR_DR;
+       if (info->flags & ASYNC_INITIALIZED) {
+               /* disable receive interrupts */
+               custom.intena = IF_RBF;
+               mb();
+               /* clear any pending receive interrupt */
+               custom.intreq = IF_RBF;
+               mb();
+
+               /*
+                * Before we drop DTR, make sure the UART transmitter
+                * has completely drained; this is especially
+                * important if there is a transmit FIFO!
+                */
+               rs_wait_until_sent(tty, info->timeout);
+       }
+       shutdown(info);
+       if (tty->driver.flush_buffer)
+               tty->driver.flush_buffer(tty);
+       if (tty->ldisc.flush_buffer)
+               tty->ldisc.flush_buffer(tty);
+       tty->closing = 0;
+       info->event = 0;
+       info->tty = 0;
+       if (info->blocked_open) {
+               if (info->close_delay) {
+                       current->state = TASK_INTERRUPTIBLE;
+                       schedule_timeout(info->close_delay);
+               }
+               wake_up_interruptible(&info->open_wait);
+       }
+       info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+                        ASYNC_CLOSING);
+       wake_up_interruptible(&info->close_wait);
+       MOD_DEC_USE_COUNT;
+       restore_flags(flags);
+}
+
+/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       struct async_struct * info = (struct async_struct *)tty->driver_data;
+       unsigned long orig_jiffies, char_time;
+       int lsr;
+       
+       if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent"))
+               return;
+
+       if (info->xmit_fifo_size == 0)
+               return; /* Just in case.... */
+
+       orig_jiffies = jiffies;
+       /*
+        * Set the check interval to be 1/5 of the estimated time to
+        * send a single character, and make it at least 1.  The check
+        * interval should also be less than the timeout.
+        * 
+        * Note: we have to use pretty tight timings here to satisfy
+        * the NIST-PCTS.
+        */
+       char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
+       char_time = char_time / 5;
+       if (char_time == 0)
+               char_time = 1;
+       if (timeout)
+         char_time = MIN(char_time, timeout);
+       /*
+        * If the transmitter hasn't cleared in twice the approximate
+        * amount of time to send the entire FIFO, it probably won't
+        * ever clear.  This assumes the UART isn't doing flow
+        * control, which is currently the case.  Hence, if it ever
+        * takes longer than info->timeout, this is probably due to a
+        * UART bug of some kind.  So, we clamp the timeout parameter at
+        * 2*info->timeout.
+        */
+       if (!timeout || timeout > 2*info->timeout)
+               timeout = 2*info->timeout;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+       printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
+       printk("jiff=%lu...", jiffies);
+#endif
+       while(!((lsr = custom.serdatr) & SDR_TSRE)) {
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+               printk("serdatr = %d (jiff=%lu)...", lsr, jiffies);
+#endif
+               current->state = TASK_INTERRUPTIBLE;
+               schedule_timeout(char_time);
+               if (signal_pending(current))
+                       break;
+               if (timeout && time_after(jiffies, orig_jiffies + timeout))
+                       break;
+       }
+       current->state = TASK_RUNNING;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+       printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+#endif
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void rs_hangup(struct tty_struct *tty)
+{
+       struct async_struct * info = (struct async_struct *)tty->driver_data;
+       struct serial_state *state = info->state;
+       
+       if (serial_paranoia_check(info, tty->device, "rs_hangup"))
+               return;
+
+       state = info->state;
+       
+       rs_flush_buffer(tty);
+       shutdown(info);
+       info->event = 0;
+       state->count = 0;
+       info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+       info->tty = 0;
+       wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+                          struct async_struct *info)
+{
+#ifdef DECLARE_WAITQUEUE
+       DECLARE_WAITQUEUE(wait, current);
+#else
+       struct wait_queue wait = { current, NULL };
+#endif
+       struct serial_state *state = info->state;
+       int             retval;
+       int             do_clocal = 0, extra_count = 0;
+       unsigned long   flags;
+
+       /*
+        * If the device is in the middle of being closed, then block
+        * until it's done, and then try again.
+        */
+       if (tty_hung_up_p(filp) ||
+           (info->flags & ASYNC_CLOSING)) {
+               if (info->flags & ASYNC_CLOSING)
+                       interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+               return ((info->flags & ASYNC_HUP_NOTIFY) ?
+                       -EAGAIN : -ERESTARTSYS);
+#else
+               return -EAGAIN;
+#endif
+       }
+
+       /*
+        * If this is a callout device, then just make sure the normal
+        * device isn't being used.
+        */
+       if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+               if (info->flags & ASYNC_NORMAL_ACTIVE)
+                       return -EBUSY;
+               if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+                   (info->flags & ASYNC_SESSION_LOCKOUT) &&
+                   (info->session != current->session))
+                   return -EBUSY;
+               if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+                   (info->flags & ASYNC_PGRP_LOCKOUT) &&
+                   (info->pgrp != current->pgrp))
+                   return -EBUSY;
+               info->flags |= ASYNC_CALLOUT_ACTIVE;
+               return 0;
+       }
+       
+       /*
+        * If non-blocking mode is set, or the port is not enabled,
+        * then make the check up front and then exit.
+        */
+       if ((filp->f_flags & O_NONBLOCK) ||
+           (tty->flags & (1 << TTY_IO_ERROR))) {
+               if (info->flags & ASYNC_CALLOUT_ACTIVE)
+                       return -EBUSY;
+               info->flags |= ASYNC_NORMAL_ACTIVE;
+               return 0;
+       }
+
+       if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+               if (state->normal_termios.c_cflag & CLOCAL)
+                       do_clocal = 1;
+       } else {
+               if (tty->termios->c_cflag & CLOCAL)
+                       do_clocal = 1;
+       }
+       
+       /*
+        * Block waiting for the carrier detect and the line to become
+        * free (i.e., not in use by the callout).  While we are in
+        * this loop, state->count is dropped by one, so that
+        * rs_close() knows when to free things.  We restore it upon
+        * exit, either normal or abnormal.
+        */
+       retval = 0;
+       add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+       printk("block_til_ready before block: ttys%d, count = %d\n",
+              state->line, state->count);
+#endif
+       save_flags(flags); cli();
+       if (!tty_hung_up_p(filp)) {
+               extra_count = 1;
+               state->count--;
+       }
+       restore_flags(flags);
+       info->blocked_open++;
+       while (1) {
+               save_flags(flags); cli();
+               if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+                   (tty->termios->c_cflag & CBAUD))
+                       rtsdtr_ctrl(SER_DTR|SER_RTS);
+               restore_flags(flags);
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (tty_hung_up_p(filp) ||
+                   !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+                       if (info->flags & ASYNC_HUP_NOTIFY)
+                               retval = -EAGAIN;
+                       else
+                               retval = -ERESTARTSYS;  
+#else
+                       retval = -EAGAIN;
+#endif
+                       break;
+               }
+               if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+                   !(info->flags & ASYNC_CLOSING) &&
+                   (do_clocal || (!(ciab.pra & SER_DCD)) ))
+                       break;
+               if (signal_pending(current)) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+#ifdef SERIAL_DEBUG_OPEN
+               printk("block_til_ready blocking: ttys%d, count = %d\n",
+                      info->line, state->count);
+#endif
+               schedule();
+       }
+       current->state = TASK_RUNNING;
+       remove_wait_queue(&info->open_wait, &wait);
+       if (extra_count)
+               state->count++;
+       info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+       printk("block_til_ready after blocking: ttys%d, count = %d\n",
+              info->line, state->count);
+#endif
+       if (retval)
+               return retval;
+       info->flags |= ASYNC_NORMAL_ACTIVE;
+       return 0;
+}
+
+static int get_async_struct(int line, struct async_struct **ret_info)
+{
+       struct async_struct *info;
+       struct serial_state *sstate;
+
+       sstate = rs_table + line;
+       sstate->count++;
+       if (sstate->info) {
+               *ret_info = sstate->info;
+               return 0;
+       }
+       info = kmalloc(sizeof(struct async_struct), GFP_KERNEL);
+       if (!info) {
+               sstate->count--;
+               return -ENOMEM;
+       }
+       memset(info, 0, sizeof(struct async_struct));
+#ifdef DECLARE_WAITQUEUE
+       init_waitqueue_head(&info->open_wait);
+       init_waitqueue_head(&info->close_wait);
+       init_waitqueue_head(&info->delta_msr_wait);
+#endif
+       info->magic = SERIAL_MAGIC;
+       info->port = sstate->port;
+       info->flags = sstate->flags;
+       info->xmit_fifo_size = sstate->xmit_fifo_size;
+       info->line = line;
+       info->tqueue.routine = do_softint;
+       info->tqueue.data = info;
+       info->state = sstate;
+       if (sstate->info) {
+               kfree_s(info, sizeof(struct async_struct));
+               *ret_info = sstate->info;
+               return 0;
+       }
+       *ret_info = sstate->info = info;
+       return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int rs_open(struct tty_struct *tty, struct file * filp)
+{
+       struct async_struct     *info;
+       int                     retval, line;
+       unsigned long           page;
+
+       MOD_INC_USE_COUNT;
+       line = MINOR(tty->device) - tty->driver.minor_start;
+       if ((line < 0) || (line >= NR_PORTS)) {
+               MOD_DEC_USE_COUNT;
+               return -ENODEV;
+       }
+       retval = get_async_struct(line, &info);
+       if (retval) {
+               MOD_DEC_USE_COUNT;
+               return retval;
+       }
+       tty->driver_data = info;
+       info->tty = tty;
+       if (serial_paranoia_check(info, tty->device, "rs_open"))
+               return -ENODEV;
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+              info->state->count);
+#endif
+       info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+       if (!tmp_buf) {
+               page = get_free_page(GFP_KERNEL);
+               if (!page) {
+                       return -ENOMEM;
+               }
+               if (tmp_buf)
+                       free_page(page);
+               else
+                       tmp_buf = (unsigned char *) page;
+       }
+
+       /*
+        * If the port is the middle of closing, bail out now
+        */
+       if (tty_hung_up_p(filp) ||
+           (info->flags & ASYNC_CLOSING)) {
+               if (info->flags & ASYNC_CLOSING)
+                       interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+               return ((info->flags & ASYNC_HUP_NOTIFY) ?
+                       -EAGAIN : -ERESTARTSYS);
+#else
+               return -EAGAIN;
+#endif
+       }
+
+       /*
+        * Start up serial port
+        */
+       retval = startup(info);
+       if (retval) {
+               return retval;
+       }
+
+       retval = block_til_ready(tty, filp, info);
+       if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+               printk("rs_open returning after block_til_ready with %d\n",
+                      retval);
+#endif
+               return retval;
+       }
+
+       if ((info->state->count == 1) &&
+           (info->flags & ASYNC_SPLIT_TERMIOS)) {
+               if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+                       *tty->termios = info->state->normal_termios;
+               else 
+                       *tty->termios = info->state->callout_termios;
+               change_speed(info, 0);
+       }
+       info->session = current->session;
+       info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("rs_open ttys%d successful...", info->line);
+#endif
+       return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+static inline int line_info(char *buf, struct serial_state *state)
+{
+       struct async_struct *info = state->info, scr_info;
+       char    stat_buf[30], control, status;
+       int     ret;
+       unsigned long flags;
+
+       ret = sprintf(buf, "%d: uart:amiga_builtin",state->line);
+
+       /*
+        * Figure out the current RS-232 lines
+        */
+       if (!info) {
+               info = &scr_info;       /* This is just for serial_{in,out} */
+
+               info->magic = SERIAL_MAGIC;
+               info->flags = state->flags;
+               info->quot = 0;
+               info->tty = 0;
+       }
+       save_flags(flags); cli();
+       status = ciab.pra;
+       control = info ? info->MCR : status;
+       restore_flags(flags); 
+       
+       stat_buf[0] = 0;
+       stat_buf[1] = 0;
+       if(!(control & SER_RTS))
+               strcat(stat_buf, "|RTS");
+       if(!(status & SER_CTS))
+               strcat(stat_buf, "|CTS");
+       if(!(control & SER_DTR))
+               strcat(stat_buf, "|DTR");
+       if(!(status & SER_DSR))
+               strcat(stat_buf, "|DSR");
+       if(!(status & SER_DCD))
+               strcat(stat_buf, "|CD");
+
+       if (info->quot) {
+               ret += sprintf(buf+ret, " baud:%d",
+                              state->baud_base / info->quot);
+       }
+
+       ret += sprintf(buf+ret, " tx:%d rx:%d",
+                     state->icount.tx, state->icount.rx);
+
+       if (state->icount.frame)
+               ret += sprintf(buf+ret, " fe:%d", state->icount.frame);
+       
+       if (state->icount.parity)
+               ret += sprintf(buf+ret, " pe:%d", state->icount.parity);
+       
+       if (state->icount.brk)
+               ret += sprintf(buf+ret, " brk:%d", state->icount.brk);  
+
+       if (state->icount.overrun)
+               ret += sprintf(buf+ret, " oe:%d", state->icount.overrun);
+
+       /*
+        * Last thing is the RS-232 status lines
+        */
+       ret += sprintf(buf+ret, " %s\n", stat_buf+1);
+       return ret;
+}
+
+int rs_read_proc(char *page, char **start, off_t off, int count,
+                int *eof, void *data)
+{
+       int len = 0, l;
+       off_t   begin = 0;
+
+       len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version);
+       l = line_info(page + len, &rs_table[0]);
+       len += l;
+       if (len+begin > off+count)
+         goto done;
+       if (len+begin < off) {
+         begin += len;
+         len = 0;
+       }
+       *eof = 1;
+done:
+       if (off >= len+begin)
+               return 0;
+       *start = page + (begin-off);
+       return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * rs_init() and friends
+ *
+ * rs_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static _INLINE_ void show_serial_version(void)
+{
+       printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
+}
+
+
+int register_serial(struct serial_struct *req);
+void unregister_serial(int line);
+
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+int __init rs_init(void)
+{
+       unsigned long flags;
+       struct serial_state * state;
+
+       if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_SERIAL))
+               return -ENODEV;
+
+       init_bh(SERIAL_BH, do_serial_bh);
+
+       IRQ_ports = NULL;
+
+       show_serial_version();
+
+       /* Initialize the tty_driver structure */
+       
+       memset(&serial_driver, 0, sizeof(struct tty_driver));
+       serial_driver.magic = TTY_DRIVER_MAGIC;
+       serial_driver.driver_name = "amiserial";
+       serial_driver.name = "ttyS";
+       serial_driver.major = TTY_MAJOR;
+       serial_driver.minor_start = 64;
+       serial_driver.num = 1;
+       serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+       serial_driver.subtype = SERIAL_TYPE_NORMAL;
+       serial_driver.init_termios = tty_std_termios;
+       serial_driver.init_termios.c_cflag =
+               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       serial_driver.flags = TTY_DRIVER_REAL_RAW;
+       serial_driver.refcount = &serial_refcount;
+       serial_driver.table = serial_table;
+       serial_driver.termios = serial_termios;
+       serial_driver.termios_locked = serial_termios_locked;
+
+       serial_driver.open = rs_open;
+       serial_driver.close = rs_close;
+       serial_driver.write = rs_write;
+       serial_driver.put_char = rs_put_char;
+       serial_driver.flush_chars = rs_flush_chars;
+       serial_driver.write_room = rs_write_room;
+       serial_driver.chars_in_buffer = rs_chars_in_buffer;
+       serial_driver.flush_buffer = rs_flush_buffer;
+       serial_driver.ioctl = rs_ioctl;
+       serial_driver.throttle = rs_throttle;
+       serial_driver.unthrottle = rs_unthrottle;
+       serial_driver.set_termios = rs_set_termios;
+       serial_driver.stop = rs_stop;
+       serial_driver.start = rs_start;
+       serial_driver.hangup = rs_hangup;
+       serial_driver.break_ctl = rs_break;
+       serial_driver.send_xchar = rs_send_xchar;
+       serial_driver.wait_until_sent = rs_wait_until_sent;
+       serial_driver.read_proc = rs_read_proc;
+       
+       /*
+        * The callout device is just like normal device except for
+        * major number and the subtype code.
+        */
+       callout_driver = serial_driver;
+       callout_driver.name = "cua";
+       callout_driver.major = TTYAUX_MAJOR;
+       callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+       callout_driver.read_proc = 0;
+       callout_driver.proc_entry = 0;
+
+       if (tty_register_driver(&serial_driver))
+               panic("Couldn't register serial driver\n");
+       if (tty_register_driver(&callout_driver))
+               panic("Couldn't register callout driver\n");
+       
+       state = rs_table;
+       state->magic = SSTATE_MAGIC;
+       state->port = (int)&custom.serdatr; /* Just to give it a value */
+       state->line = 0;
+       state->custom_divisor = 0;
+       state->close_delay = 5*HZ/10;
+       state->closing_wait = 30*HZ;
+       state->callout_termios = callout_driver.init_termios;
+       state->normal_termios = serial_driver.init_termios;
+       state->icount.cts = state->icount.dsr = 
+         state->icount.rng = state->icount.dcd = 0;
+       state->icount.rx = state->icount.tx = 0;
+       state->icount.frame = state->icount.parity = 0;
+       state->icount.overrun = state->icount.brk = 0;
+       /*
+       if(state->port && check_region(state->port,REGION_LENGTH(state)))
+         continue;
+       */
+
+       printk(KERN_INFO "ttyS%02d is the amiga builtin serial port\n",
+                      state->line);
+
+       /* Hardware set up */
+
+       state->baud_base = amiga_colorclock;
+       state->xmit_fifo_size = 1;
+
+       save_flags (flags);
+       cli();
+
+       /* set ISRs, and then disable the rx interrupts */
+       request_irq(IRQ_AMIGA_TBE, ser_tx_int, 0, "serial TX", state);
+       request_irq(IRQ_AMIGA_RBF, ser_rx_int, SA_INTERRUPT, "serial RX", state);
+
+       /* turn off Rx and Tx interrupts */
+       custom.intena = IF_RBF | IF_TBE;
+       mb();
+
+       /* clear any pending interrupt */
+       custom.intreq = IF_RBF | IF_TBE;
+       mb();
+
+       restore_flags (flags);
+
+       /*
+        * set the appropriate directions for the modem control flags,
+        * and clear RTS and DTR
+        */
+       ciab.ddra |= (SER_DTR | SER_RTS);   /* outputs */
+       ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR);  /* inputs */
+
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       return rs_init();
+}
+
+void cleanup_module(void) 
+{
+       unsigned long flags;
+       int e1, e2;
+       struct async_struct *info;
+
+       /* printk("Unloading %s: version %s\n", serial_name, serial_version); */
+       save_flags(flags);
+       cli();
+        remove_bh(SERIAL_BH);
+       if ((e1 = tty_unregister_driver(&serial_driver)))
+               printk("SERIAL: failed to unregister serial driver (%d)\n",
+                      e1);
+       if ((e2 = tty_unregister_driver(&callout_driver)))
+               printk("SERIAL: failed to unregister callout driver (%d)\n", 
+                      e2);
+       restore_flags(flags);
+
+       info = rs_table[0].info;
+       if (info) {
+         rs_table[0].info = NULL;
+         kfree_s(info, sizeof(struct async_struct));
+       }
+
+       if (tmp_buf) {
+               free_page((unsigned long) tmp_buf);
+               tmp_buf = NULL;
+       }
+}
+#endif /* MODULE */
+
+/*
+  Local variables:
+  compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce  -DCPU=686 -march=i686 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h   -DEXPORT_SYMTAB -c serial.c"
+  End:
+*/
diff --git a/drivers/char/mac_SCC.c b/drivers/char/mac_SCC.c
deleted file mode 100644 (file)
index c20e7a9..0000000
+++ /dev/null
@@ -1,1529 +0,0 @@
-/*
- * mac_SCC.c: m68k version of
- *
- * macserial.c: Serial port driver for Power Macintoshes.
- *             Extended for the 68K mac by Alan Cox.
- *              Rewritten to m68k serial design by Michael Schmitz
- *
- * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras.
- *
- * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- */
-
-/*
- * Design note for the m68k rewrite:
- * The structure of the m68k serial code requires separation of the low-level
- * functions that talk directly to the hardware from the Linux serial driver 
- * code interfacing to the tty layer. The reason for this separation is simply
- * the fact that the m68k serial hardware is, unlike the i386, based on a 
- * variety of chips, and the rs_* serial routines need to be shared.
- *
- * I've tried to make consistent use of the async_struct info populated in the
- * midlevel code, and introduced an async_private struct to hold the Macintosh 
- * SCC internals (this was added to the async_struct for the PowerMac driver).
- * Exception: the console and kgdb hooks still use the zs_soft[] data, and this
- * is still filled in by the probe_sccs() routine, which provides some data 
- * for mac_SCC_init as well. Interrupts are registered in mac_SCC_init, so 
- * the console/kgdb stuff probably won't work before proper serial init, and 
- * I have to rely on keeping info and zs_soft consistent at least for the 
- * console/kgdb port.
- *
- * Update (16-11-97): The SCC interrupt handling was suffering from the problem
- * that the autovector SCC interrupt was registered only once, hence only one
- * async_struct was passed to the interrupt function and only interrupts from 
- * the corresponding channel could be handled (yes, major design flaw).
- * The autovector interrupt is now registered by the main interrupt initfunc, 
- * and uses a handler that will call the registered SCC specific interrupts in 
- * turn. The SCC init has to register these as machspec interrupts now, as is 
- * done for the VIA interrupts elsewhere.
- */
-
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/config.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-
-#include <asm/uaccess.h>
-#include <asm/setup.h>
-#include <asm/bootinfo.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/macints.h>
-#ifndef CONFIG_MAC
-#include <asm/prom.h>
-#endif
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <asm/bitops.h>
-#include <asm/hwtest.h>
-
-#include "mac_SCC.h"
-
-/*
- * It would be nice to dynamically allocate everything that
- * depends on NUM_SERIAL, so we could support any number of
- * Z8530s, but for now...
- */
-#define NUM_SERIAL     2               /* Max number of ZS chips supported */
-#define NUM_CHANNELS   (NUM_SERIAL * 2)        /* 2 channels per chip */
-
-#ifdef CONFIG_MAC
-/*
- *     All the Macintosh 68K boxes that have an MMU also have hardware
- *     recovery delays.
- */
-#define RECOVERY_DELAY
-#else
-/* On PowerMacs, the hardware takes care of the SCC recovery time,
-   but we need the eieio to make sure that the accesses occur
-   in the order we want. */
-#define RECOVERY_DELAY eieio()
-#endif
-
-struct mac_zschannel *zs_kgdbchan;
-struct mac_zschannel zs_channels[NUM_CHANNELS];
-
-struct m68k_async_struct  zs_soft[NUM_CHANNELS];
-struct m68k_async_private zs_soft_private[NUM_CHANNELS];
-int zs_channels_found;
-struct m68k_async_struct *zs_chain;    /* list of all channels */
-
-struct tty_struct zs_ttys[NUM_CHANNELS];
-/** struct tty_struct *zs_constty; **/
-
-/* Console hooks... */
-static int zs_cons_chanout = 0;
-static int zs_cons_chanin = 0;
-struct m68k_async_struct  *zs_consinfo = 0;
-struct mac_zschannel *zs_conschan;
-
-static unsigned char kgdb_regs[16] = {
-       0, 0, 0,                /* write 0, 1, 2 */
-       (Rx8 | RxENABLE),       /* write 3 */
-       (X16CLK | SB1 | PAR_EVEN), /* write 4 */
-       (Tx8 | TxENAB),         /* write 5 */
-       0, 0, 0,                /* write 6, 7, 8 */
-       (NV),                   /* write 9 */
-       (NRZ),                  /* write 10 */
-       (TCBR | RCBR),          /* write 11 */
-       1, 0,                   /* 38400 baud divisor, write 12 + 13 */
-       (BRENABL),              /* write 14 */
-       (DCDIE)                 /* write 15 */
-};
-
-#define ZS_CLOCK         3686400       /* Z8530 RTxC input clock rate */
-
-/* Debugging... DEBUG_INTR is bad to use when one of the zs
- * lines is your console ;(
- */
-#undef SERIAL_DEBUG_INTR
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_FLOW
-
-#define RS_STROBE_TIME 10
-#define RS_ISR_PASS_LIMIT 256
-
-#define _INLINE_ inline
-
-static void probe_sccs(void);
-
-#ifndef MIN
-#define MIN(a,b)       ((a) < (b) ? (a) : (b))
-#endif
-
-
-/***************************** Prototypes *****************************/
-
-static void SCC_init_port( struct m68k_async_struct *info, int type, int channel );
-#if 0
-#ifdef MODULE
-static void SCC_deinit_port( struct m68k_async_struct *info, int channel );
-#endif
-#endif
-
-/* FIXME !!! Currently, only autovector interrupt used! */
-#if 0
-static void SCC_rx_int (int irq, void *data, struct pt_regs *fp);
-static void SCC_spcond_int (int irq, void *data, struct pt_regs *fp);
-static void SCC_tx_int (int irq, void *data, struct pt_regs *fp);
-static void SCC_stat_int (int irq, void *data, struct pt_regs *fp);
-static void SCC_ri_int (int irq, void *data, struct pt_regs *fp);
-#endif
-
-static int SCC_check_open( struct m68k_async_struct *info, struct tty_struct
-                           *tty, struct file *file );
-static void SCC_init( struct m68k_async_struct *info );
-static void SCC_deinit( struct m68k_async_struct *info, int leave_dtr );
-static void SCC_enab_tx_int( struct m68k_async_struct *info, int enab_flag );
-static int SCC_check_custom_divisor( struct m68k_async_struct *info, int baud_base,
-                                   int divisor );
-static void SCC_change_speed( struct m68k_async_struct *info );
-#if 0
-static int SCC_clocksrc( unsigned baud_base, unsigned channel );
-#endif
-static void SCC_throttle( struct m68k_async_struct *info, int status );
-static void SCC_set_break( struct m68k_async_struct *info, int break_flag );
-static void SCC_get_serial_info( struct m68k_async_struct *info, struct
-                               serial_struct *retinfo );
-static unsigned int SCC_get_modem_info( struct m68k_async_struct *info );
-static int SCC_set_modem_info( struct m68k_async_struct *info, int new_dtr, int
-                             new_rts );
-static int SCC_ioctl( struct tty_struct *tty, struct file *file, struct
-                    m68k_async_struct *info, unsigned int cmd, unsigned long arg );
-static void SCC_stop_receive (struct m68k_async_struct *info);
-static int SCC_trans_empty (struct m68k_async_struct *info);
-
-/************************* End of Prototypes **************************/
-
-
-static SERIALSWITCH SCC_switch = {
-       SCC_init, SCC_deinit, SCC_enab_tx_int,
-       SCC_check_custom_divisor, SCC_change_speed,
-       SCC_throttle, SCC_set_break,
-       SCC_get_serial_info, SCC_get_modem_info,
-       SCC_set_modem_info, SCC_ioctl, SCC_stop_receive, SCC_trans_empty,
-       SCC_check_open
-};
-
-/*
- * This is used to figure out the divisor speeds and the timeouts
- */
-static int baud_table[] = {
-       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-       9600, 19200, 38400, 57600, 115200, 0 };
-
-/* 
- * Reading and writing Z8530 registers.
- */
-static inline unsigned char read_zsreg(struct mac_zschannel *channel,
-                                      unsigned char reg)
-{
-       unsigned char retval;
-
-       if (reg != 0) {
-               *channel->control = reg;
-               RECOVERY_DELAY;
-       }
-       retval = *channel->control;
-       RECOVERY_DELAY;
-       return retval;
-}
-
-static inline void write_zsreg(struct mac_zschannel *channel,
-                              unsigned char reg, unsigned char value)
-{
-       if (reg != 0) {
-               *channel->control = reg;
-               RECOVERY_DELAY;
-       }
-       *channel->control = value;
-       RECOVERY_DELAY;
-       return;
-}
-
-static inline unsigned char read_zsdata(struct mac_zschannel *channel)
-{
-       unsigned char retval;
-
-       retval = *channel->data;
-       RECOVERY_DELAY;
-       return retval;
-}
-
-static inline void write_zsdata(struct mac_zschannel *channel,
-                               unsigned char value)
-{
-       *channel->data = value;
-       RECOVERY_DELAY;
-       return;
-}
-
-static inline void load_zsregs(struct mac_zschannel *channel,
-                              unsigned char *regs)
-{
-       ZS_CLEARERR(channel);
-       ZS_CLEARFIFO(channel);
-       /* Load 'em up */
-       write_zsreg(channel, R4, regs[R4]);
-       write_zsreg(channel, R10, regs[R10]);
-       write_zsreg(channel, R3, regs[R3] & ~RxENABLE);
-       write_zsreg(channel, R5, regs[R5] & ~TxENAB);
-       write_zsreg(channel, R1, regs[R1]);
-       write_zsreg(channel, R9, regs[R9]);
-       write_zsreg(channel, R11, regs[R11]);
-       write_zsreg(channel, R12, regs[R12]);
-       write_zsreg(channel, R13, regs[R13]);
-       write_zsreg(channel, R14, regs[R14]);
-       write_zsreg(channel, R15, regs[R15]);
-       write_zsreg(channel, R3, regs[R3]);
-       write_zsreg(channel, R5, regs[R5]);
-       return;
-}
-
-/* Sets or clears DTR/RTS on the requested line */
-static inline void zs_rtsdtr(struct m68k_async_struct *ss, int set)
-{
-       if (set)
-               ss->private->curregs[5] |= (RTS | DTR);
-       else
-               ss->private->curregs[5] &= ~(RTS | DTR);
-       write_zsreg(ss->private->zs_channel, 5, ss->private->curregs[5]);
-       return;
-}
-
-static inline void kgdb_chaninit(struct m68k_async_struct *ss, int intson, int bps)
-{
-       int brg;
-
-       if (intson) {
-               kgdb_regs[R1] = INT_ALL_Rx;
-               kgdb_regs[R9] |= MIE;
-       } else {
-               kgdb_regs[R1] = 0;
-               kgdb_regs[R9] &= ~MIE;
-       }
-       brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
-       kgdb_regs[R12] = brg;
-       kgdb_regs[R13] = brg >> 8;
-       load_zsregs(ss->private->zs_channel, kgdb_regs);
-}
-
-/* Utility routines for the Zilog */
-static inline int get_zsbaud(struct m68k_async_struct *ss)
-{
-       struct mac_zschannel *channel = ss->private->zs_channel;
-       int brg;
-
-       /* The baud rate is split up between two 8-bit registers in
-        * what is termed 'BRG time constant' format in my docs for
-        * the chip, it is a function of the clk rate the chip is
-        * receiving which happens to be constant.
-        */
-       brg = (read_zsreg(channel, 13) << 8);
-       brg |= read_zsreg(channel, 12);
-       return BRG_TO_BPS(brg, (ZS_CLOCK/(ss->private->clk_divisor)));
-}
-
-/* On receive, this clears errors and the receiver interrupts */
-static inline void SCC_recv_clear(struct mac_zschannel *zsc)
-{
-       write_zsreg(zsc, 0, ERR_RES);
-       write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */
-}
-
-/*
- * ----------------------------------------------------------------------
- *
- * Here starts the interrupt handling routines.  All of the following
- * subroutines are declared as inline and are folded into
- * rs_interrupt().  They were separated out for readability's sake.
- *
- *                             - Ted Ts'o (tytso@mit.edu), 7-Mar-93
- * -----------------------------------------------------------------------
- */
-
-extern void breakpoint(void);  /* For the KGDB frame character */
-
-static /*_INLINE_*/ void receive_chars(struct m68k_async_struct *info,
-                                  struct pt_regs *regs)
-{
-       struct tty_struct *tty = info->tty;
-       unsigned char ch, stat, flag;
-
-       while ((read_zsreg(info->private->zs_channel, 0) & Rx_CH_AV) != 0) {
-
-               stat = read_zsreg(info->private->zs_channel, R1);
-               ch = read_zsdata(info->private->zs_channel);
-
-#ifdef SCC_DEBUG
-               printk("mac_SCC: receive_chars stat=%X char=%X \n", stat, ch);
-#endif
-
-#if 0  /* KGDB not yet supported */
-               /* Look for kgdb 'stop' character, consult the gdb documentation
-                * for remote target debugging and arch/sparc/kernel/sparc-stub.c
-                * to see how all this works.
-                */
-               if ((info->kgdb_channel) && (ch =='\003')) {
-                       breakpoint();
-                       continue;
-               }
-#endif
-
-               if (!tty)
-                       continue;
-
-               if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-                       tty_flip_buffer_push(tty);
-               
-               if (stat & Rx_OVR) {
-                       flag = TTY_OVERRUN;
-                       /* reset the error indication */
-                       write_zsreg(info->private->zs_channel, 0, ERR_RES);
-               } else if (stat & FRM_ERR) {
-                       /* this error is not sticky */
-                       flag = TTY_FRAME;
-               } else if (stat & PAR_ERR) {
-                       flag = TTY_PARITY;
-                       /* reset the error indication */
-                       write_zsreg(info->private->zs_channel, 0, ERR_RES);
-               } else
-                       flag = 0;
-
-               if (tty->flip.buf_num 
-                   && tty->flip.count >= TTY_FLIPBUF_SIZE) {
-#ifdef SCC_DEBUG_OVERRUN
-                       printk("mac_SCC: flip buffer overrun!\n");
-#endif
-                       return;
-               }
-
-               if (!tty->flip.buf_num 
-                   && tty->flip.count >= 2*TTY_FLIPBUF_SIZE) {
-                       printk("mac_SCC: double flip buffer overrun!\n");
-                       return;
-               }
-
-               tty->flip.count++;
-               *tty->flip.flag_buf_ptr++ = flag;
-               *tty->flip.char_buf_ptr++ = ch;
-               info->icount.rx++;
-               tty_flip_buffer_push(tty);
-       }
-#if 0
-clear_and_exit:
-       SCC_recv_clear(info->private->zs_channel);
-#endif
-}
-
-/* that's SCC_enable_tx_int, basically */
-
-static void transmit_chars(struct m68k_async_struct *info)
-{
-       if ((read_zsreg(info->private->zs_channel, 0) & Tx_BUF_EMP) == 0)
-               return;
-       info->private->tx_active = 0;
-
-       if (info->x_char) {
-               /* Send next char */
-               write_zsdata(info->private->zs_channel, info->x_char);
-               info->x_char = 0;
-               info->private->tx_active = 1;
-               return;
-       }
-
-       if ((info->xmit_cnt <= 0) || info->tty->stopped 
-            || info->private->tx_stopped) {
-               write_zsreg(info->private->zs_channel, 0, RES_Tx_P);
-               return;
-       }
-
-       /* Send char */
-       write_zsdata(info->private->zs_channel, info->xmit_buf[info->xmit_tail++]);
-       info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
-       info->icount.tx++;
-       info->xmit_cnt--;
-       info->private->tx_active = 1;
-
-       if (info->xmit_cnt < WAKEUP_CHARS)
-               rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
-}
-
-static /*_INLINE_*/ void status_handle(struct m68k_async_struct *info)
-{
-       unsigned char status;
-
-       /* Get status from Read Register 0 */
-       status = read_zsreg(info->private->zs_channel, 0);
-
-       /* Check for DCD transitions */
-       if (((status ^ info->private->read_reg_zero) & DCD) != 0
-           && info->tty && C_CLOCAL(info->tty)) {
-               if (status & DCD) {
-                       wake_up_interruptible(&info->open_wait);
-               } else if (!(info->flags & ZILOG_CALLOUT_ACTIVE)) {
-                       if (info->tty)
-                               tty_hangup(info->tty);
-               }
-       }
-
-       /* Check for CTS transitions */
-       if (info->tty && C_CRTSCTS(info->tty)) {
-               /*
-                * For some reason, on the Power Macintosh,
-                * it seems that the CTS bit is 1 when CTS is
-                * *negated* and 0 when it is asserted.
-                * The DCD bit doesn't seem to be inverted
-                * like this.
-                */
-               if ((status & CTS) == 0) {
-                       if (info->private->tx_stopped) {
-                               info->private->tx_stopped = 0;
-                               if (!info->private->tx_active)
-                                       transmit_chars(info);
-                       }
-               } else {
-                       info->private->tx_stopped = 1;
-               }
-       }
-
-       /* Clear status condition... */
-       write_zsreg(info->private->zs_channel, 0, RES_EXT_INT);
-       info->private->read_reg_zero = status;
-}
-
-/*
- * This is the serial driver's generic interrupt routine
- */
-void mac_SCC_interrupt(int irq, void *dev_id, struct pt_regs * regs)
-{
-       struct m68k_async_struct *info = (struct m68k_async_struct *) dev_id;
-       unsigned char zs_intreg;
-       int shift;
-
-       /* NOTE: The read register 3, which holds the irq status,
-        *       does so for both channels on each chip.  Although
-        *       the status value itself must be read from the A
-        *       channel and is only valid when read from channel A.
-        *       Yes... broken hardware...
-        */
-#define CHAN_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT)
-
-#ifdef SCC_DEBUG
-       printk("mac_SCC: interrupt; port: %lx channel: %lx \n", 
-               info->port, info->private->zs_channel);
-#endif
-
-       if (info->private->zs_chan_a == info->private->zs_channel)
-               shift = 3;      /* Channel A */
-       else
-               shift = 0;      /* Channel B */
-
-       for (;;) {
-               zs_intreg = read_zsreg(info->private->zs_chan_a, 3);
-#ifdef SCC_DEBUG
-               printk("mac_SCC: status %x shift %d shifted %x \n", 
-               zs_intreg, shift, zs_intreg >> shift);
-#endif
-               zs_intreg = zs_intreg >> shift;
-               if ((zs_intreg & CHAN_IRQMASK) == 0)
-                       break;
-
-               if (zs_intreg & CHBRxIP)
-                       receive_chars(info, regs);
-               if (zs_intreg & CHBTxIP)
-                       transmit_chars(info);
-               if (zs_intreg & CHBEXT)
-                       status_handle(info);
-       }
-}
-
-/*
- * -------------------------------------------------------------------
- * Here ends the serial interrupt routines.
- * -------------------------------------------------------------------
- */
-
-/*
- * ------------------------------------------------------------
- * rs_stop() and rs_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * ------------------------------------------------------------
- */
-
-static void SCC_enab_tx_int( struct m68k_async_struct *info, int enab_flag )
-{
-       unsigned long flags;
-
-       if (enab_flag) {
-#if 0
-               save_flags(flags); cli();
-               if (info->private->curregs[5] & TxENAB) {
-                       info->private->curregs[5] &= ~TxENAB;
-                       info->private->pendregs[5] &= ~TxENAB;
-                       write_zsreg(info->private->zs_channel, 5, 
-                                   info->private->curregs[5]);
-               }
-               restore_flags(flags);
-#endif
-       /* FIXME: should call transmit_chars here ??? */
-               transmit_chars(info);
-       } else {
-       save_flags(flags); cli();
-#if 0
-               if (  info->xmit_cnt && info->xmit_buf && 
-                   !(info->private->curregs[5] & TxENAB)) {
-                       info->private->curregs[5] |= TxENAB;
-                       info->private->pendregs[5] = info->private->curregs[5];
-                       write_zsreg(info->private->zs_channel, 5, 
-                                   info->private->curregs[5]);
-               }
-#else
-               if ( info->xmit_cnt && info->xmit_buf && 
-                   !info->private->tx_active) {
-                       transmit_chars(info);
-               }
-#endif
-               restore_flags(flags);   
-       }
-
-}
-
-#if 0
-/*
- * leftover from original driver ...
- */
-static int SCC_startup(struct m68k_async_struct * info)
-{
-       unsigned long flags;
-
-       save_flags(flags); cli();
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("starting up ttyS%d (irq %d)...", info->line, info->irq);
-#endif
-
-       /*
-        * Clear the receive FIFO.
-        */
-       ZS_CLEARFIFO(info->private->zs_channel);
-       info->xmit_fifo_size = 1;
-
-       /*
-        * Clear the interrupt registers.
-        */
-       write_zsreg(info->private->zs_channel, 0, ERR_RES);
-       write_zsreg(info->private->zs_channel, 0, RES_H_IUS);
-
-       /*
-        * Turn on RTS and DTR.
-        */
-       zs_rtsdtr(info, 1);
-
-       /*
-        * Finally, enable sequencing and interrupts
-        */
-       info->private->curregs[1] = (info->private->curregs[1] & ~0x18) 
-                                   | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
-       info->private->pendregs[1] = info->private->curregs[1];
-       info->private->curregs[3] |= (RxENABLE | Rx8);
-       info->private->pendregs[3] = info->private->curregs[3];
-       info->private->curregs[5] |= (TxENAB | Tx8);
-       info->private->pendregs[5] = info->private->curregs[5];
-       info->private->curregs[9] |= (NV | MIE);
-       info->private->pendregs[9] = info->private->curregs[9];
-       write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]);
-       write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
-       write_zsreg(info->private->zs_channel, 9, info->private->curregs[9]);
-
-       /*
-        * Set the speed of the serial port
-        */
-       SCC_change_speed(info);
-
-       /* Save the current value of RR0 */
-       info->private->read_reg_zero = read_zsreg(info->private->zs_channel, 0);
-
-       restore_flags(flags);
-       return 0;
-}
-#endif
-
-/* FIXME: are these required ?? */
-static int SCC_check_open( struct m68k_async_struct *info, struct tty_struct *tty,
-                         struct file *file )
-{
-       /* check on the basis of info->whatever ?? */
-       if (info->private->kgdb_channel || info->private->is_cons)
-               return -EBUSY;
-       return( 0 );
-}
-
-static void SCC_init( struct m68k_async_struct *info )
-{
-       /* FIXME: init currently done in probe_sccs() */
-
-       /* BUT: startup part needs to be done here! */
-
-#ifdef SCC_DEBUG
-       printk("mac_SCC: init, info %lx, info->port %lx  \n", info, info->port);
-#endif
-       /*
-        * Clear the receive FIFO.
-        */
-       ZS_CLEARFIFO(info->private->zs_channel);
-       info->xmit_fifo_size = 1;
-
-       /*
-        * Clear the interrupt registers.
-        */
-       write_zsreg(info->private->zs_channel, 0, ERR_RES);
-       write_zsreg(info->private->zs_channel, 0, RES_H_IUS);
-
-       /*
-        * Turn on RTS and DTR.
-        */
-       zs_rtsdtr(info, 1);
-
-       /*
-        * Finally, enable sequencing and interrupts
-        */
-       info->private->curregs[1] = (info->private->curregs[1] & ~0x18) 
-                                   | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
-       info->private->pendregs[1] = info->private->curregs[1];
-       info->private->curregs[3] |= (RxENABLE | Rx8);
-       info->private->pendregs[3] = info->private->curregs[3];
-       info->private->curregs[5] |= (TxENAB | Tx8);
-       info->private->pendregs[5] = info->private->curregs[5];
-       info->private->curregs[9] |= (NV | MIE);
-       info->private->pendregs[9] = info->private->curregs[9];
-       write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]);
-       write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
-       write_zsreg(info->private->zs_channel, 9, info->private->curregs[9]);
-
-       /*
-        * Set the speed of the serial port - done in startup() !!
-        */
-#if 0
-       SCC_change_speed(info);
-#endif
-
-       /* Save the current value of RR0 */
-       info->private->read_reg_zero = read_zsreg(info->private->zs_channel, 0);
-
-}
-
-static void SCC_init_port( struct m68k_async_struct *info, int type, int channel )
-{
-       static int got_autovector = 0;
-
-#ifdef SCC_DEBUG
-       printk("mac_SCC: init_port, info %x \n", info);
-#endif
-       info->sw = &SCC_switch;
-       info->private = &zs_soft_private[channel];
-       info->private->zs_channel = &zs_channels[channel];
-       info->irq = IRQ4;
-       info->private->clk_divisor = 16;
-       info->private->zs_baud = get_zsbaud(info);
-       info->port = (int) info->private->zs_channel->control;
-
-       /*
-        * MSch: Extended interrupt scheme:
-        * The generic m68k interrupt code can't use multiple handlers for
-        * the same interrupt source (no chained interrupts).
-        * We have to plug in a 'master' interrupt handler instead, calling 
-        * mac_SCC_interrupt with the proper arguments ...
-        */
-
-       if (!got_autovector) {
-               if(sys_request_irq(IRQ4, mac_SCC_handler, 0, "SCC master", info))
-                       panic("macserial: can't get irq %d", IRQ4);
-#ifdef SCC_DEBUG
-               printk("mac_SCC: got SCC master interrupt %d, channel %d info %p\n",
-                       IRQ4, channel, info);
-#endif
-               got_autovector = 1;
-       }
-
-       if (info->private->zs_chan_a == info->private->zs_channel) {
-               /* Channel A */
-               if (request_irq(IRQ_SCCA, mac_SCC_interrupt, 0, "SCC A", info))
-                       panic("mac_SCC: can't get irq %d", IRQ_SCCA);
-#ifdef SCC_DEBUG
-               printk("mac_SCC: got SCC A interrupt %d, channel %d info %p\n",
-                       IRQ_SCCA, channel, info);
-#endif
-       } else {
-               /* Channel B */
-               if (request_irq(IRQ_SCCB, mac_SCC_interrupt, 0, "SCC B", info))
-                       panic("mac_SCC: can't get irq %d", IRQ_SCCB);
-#ifdef SCC_DEBUG
-               printk("mac_SCC: got SCC B interrupt %d, channel %d info %p\n", 
-                       IRQ_SCCB, channel, info);
-#endif
-       }
-
-       /* If console serial line, then enable interrupts. */
-       if (info->private->is_cons) {
-               printk("mac_SCC: console line %d; enabling interrupt!\n", info->line);
-               write_zsreg(info->private->zs_channel, R1,
-                           (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB));
-               write_zsreg(info->private->zs_channel, R9, (NV | MIE));
-               write_zsreg(info->private->zs_channel, R10, (NRZ));
-               write_zsreg(info->private->zs_channel, R3, (Rx8 | RxENABLE));
-               write_zsreg(info->private->zs_channel, R5, (Tx8 | TxENAB));
-       }
-       /* If this is the kgdb line, enable interrupts because we
-        * now want to receive the 'control-c' character from the
-        * client attached to us asynchronously.
-        */
-       if (info->private->kgdb_channel) {
-               printk("mac_SCC: kgdb line %d; enabling interrupt!\n", info->line);
-               kgdb_chaninit(info, 1, info->private->zs_baud);
-       }
-       /* Report settings (in m68kserial.c) */
-#ifndef CONFIG_MAC
-       printk("ttyS%d at 0x%08x (irq = %d)", info->line, 
-                      info->port, info->irq);
-       printk(" is a Z8530 SCC\n");
-#endif
-
-}
-
-/*
- * This routine will shutdown a serial port; interrupts are disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
- */
-static void SCC_deinit(struct m68k_async_struct * info, int leave_dtr)
-{
-       unsigned long   flags;
-
-       save_flags(flags); cli(); /* Disable interrupts */
-       
-       info->private->pendregs[1] = info->private->curregs[1] = 0;
-       write_zsreg(info->private->zs_channel, 1, 0);   /* no interrupts */
-
-       info->private->curregs[3] &= ~RxENABLE;
-       info->private->pendregs[3] = info->private->curregs[3];
-       write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]);
-
-       info->private->curregs[5] &= ~TxENAB;
-
-       if (!leave_dtr)
-               info->private->curregs[5] &= ~(DTR | RTS);
-       else
-               info->private->curregs[5] &= ~(RTS);
-
-       info->private->pendregs[5] = info->private->curregs[5];
-       write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
-
-       restore_flags(flags);
-}
-
-/* FIXME !!! */
-static int SCC_check_custom_divisor( struct m68k_async_struct *info,
-                                   int baud_base, int divisor )
-{
-       return 0;
-}
-
-/*
- * This routine is called to set the UART divisor registers to match
- * the specified baud rate for a serial port.
- */
-static void SCC_change_speed(struct m68k_async_struct *info)
-{
-       unsigned short port;
-       unsigned cflag;
-       int     i;
-       int     brg;
-       unsigned long flags;
-
-       if (!info->tty || !info->tty->termios)
-               return;
-       cflag = info->tty->termios->c_cflag;
-       if (!(port = info->port))
-               return;
-       i = cflag & CBAUD;
-
-       if (i == 0 && !(info->flags & ASYNC_SPD_MASK)) {
-               /* speed == 0 -> drop DTR */
-               save_flags(flags);
-               cli();
-               info->private->curregs[5] &= ~(DTR | RTS);
-               write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
-               restore_flags(flags);
-               return;
-       }
-
-
-       if (i & CBAUDEX) {
-               /* XXX CBAUDEX is not obeyed.
-                * It is impossible at a 32bits PPC.  XXX??
-                * But we have to report this to user ... someday.
-                */
-               i = B9600;
-       }
-
-       save_flags(flags); cli();
-       info->private->zs_baud = baud_table[i];
-       info->private->clk_divisor = 16;
-
-       info->private->curregs[4] = X16CLK;
-       info->private->curregs[11] = TCBR | RCBR;
-       brg = BPS_TO_BRG(info->private->zs_baud, 
-                        ZS_CLOCK/info->private->clk_divisor);
-       info->private->curregs[12] = (brg & 255);
-       info->private->curregs[13] = ((brg >> 8) & 255);
-       info->private->curregs[14] = BRENABL;
-
-       /* byte size and parity */
-       info->private->curregs[3] &= ~RxNBITS_MASK;
-       info->private->curregs[5] &= ~TxNBITS_MASK;
-       switch (cflag & CSIZE) {
-       case CS5:
-               info->private->curregs[3] |= Rx5;
-               info->private->curregs[5] |= Tx5;
-               break;
-       case CS6:
-               info->private->curregs[3] |= Rx6;
-               info->private->curregs[5] |= Tx6;
-               break;
-       case CS7:
-               info->private->curregs[3] |= Rx7;
-               info->private->curregs[5] |= Tx7;
-               break;
-       case CS8:
-       default: /* defaults to 8 bits */
-               info->private->curregs[3] |= Rx8;
-               info->private->curregs[5] |= Tx8;
-               break;
-       }
-       info->private->pendregs[3] = info->private->curregs[3];
-       info->private->pendregs[5] = info->private->curregs[5];
-
-       info->private->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN);
-       if (cflag & CSTOPB) {
-               info->private->curregs[4] |= SB2;
-       } else {
-               info->private->curregs[4] |= SB1;
-       }
-       if (cflag & PARENB) {
-               info->private->curregs[4] |= PAR_ENA;
-       }
-       if (!(cflag & PARODD)) {
-               info->private->curregs[4] |= PAR_EVEN;
-       }
-       info->private->pendregs[4] = info->private->curregs[4];
-
-       info->private->curregs[15] &= ~(DCDIE | CTSIE);
-       if (!(cflag & CLOCAL)) {
-               info->private->curregs[15] |= DCDIE;
-       }
-       if (cflag & CRTSCTS) {
-               info->private->curregs[15] |= CTSIE;
-               if ((read_zsreg(info->private->zs_channel, 0) & CTS) != 0)
-                       info->private->tx_stopped = 1;
-       } else
-               info->private->tx_stopped = 0;
-       info->private->pendregs[15] = info->private->curregs[15];
-
-       /* Load up the new values */
-       load_zsregs(info->private->zs_channel, info->private->curregs);
-
-       restore_flags(flags);
-}
-
-/* This is for console output over ttya/ttyb */
-static void SCC_put_char(char ch)
-{
-       struct mac_zschannel *chan = zs_conschan;
-       int loops = 0;
-       unsigned long flags;
-
-       if(!chan)
-               return;
-
-       save_flags(flags); cli();
-       while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0 && loops < 10000) {
-               loops++;
-               udelay(5);
-       }
-       write_zsdata(chan, ch);
-       restore_flags(flags);
-}
-
-/* These are for receiving and sending characters under the kgdb
- * source level kernel debugger.
- */
-void putDebugChar(char kgdb_char)
-{
-       struct mac_zschannel *chan = zs_kgdbchan;
-
-       while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0)
-               udelay(5);
-       write_zsdata(chan, kgdb_char);
-}
-
-char getDebugChar(void)
-{
-       struct mac_zschannel *chan = zs_kgdbchan;
-
-       while ((read_zsreg(chan, 0) & Rx_CH_AV) == 0)
-               udelay(5);
-       return read_zsdata(chan);
-}
-
-/*
- * Fair output driver allows a process to speak.
- */
-static void SCC_fair_output(void)
-{
-       int left;               /* Output no more than that */
-       unsigned long flags;
-       struct m68k_async_struct *info = zs_consinfo;
-       char c;
-
-       if (info == 0) return;
-       if (info->xmit_buf == 0) return;
-
-       save_flags(flags);  cli();
-       left = info->xmit_cnt;
-       while (left != 0) {
-               c = info->xmit_buf[info->xmit_tail];
-               info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
-               info->xmit_cnt--;
-               restore_flags(flags);
-
-               SCC_put_char(c);
-
-               save_flags(flags);  cli();
-               left = MIN(info->xmit_cnt, left-1);
-       }
-
-       restore_flags(flags);
-       return;
-}
-
-/*
- * zs_console_print is registered for printk.
- */
-static void zs_console_print(const char *p)
-{
-       char c;
-
-       while ((c = *(p++)) != 0) {
-               if (c == '\n')
-                       SCC_put_char('\r');
-               SCC_put_char(c);
-       }
-
-       /* Comment this if you want to have a strict interrupt-driven output */
-       SCC_fair_output();
-}
-
-/* FIXME: check with SCC_enab_tx_int!! */
-#if 0
-static void rs_flush_chars(struct tty_struct *tty)
-{
-       struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
-               return;
-
-       if (info->xmit_cnt <= 0 || tty->stopped || info->private->tx_stopped ||
-           !info->xmit_buf)
-               return;
-
-       /* Enable transmitter */
-       save_flags(flags); cli();
-       transmit_chars(info);
-       restore_flags(flags);
-}
-
-static int rs_write(struct tty_struct * tty, int from_user,
-                   const unsigned char *buf, int count)
-{
-       int     c, total = 0;
-       struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->device, "rs_write"))
-               return 0;
-
-       if (!tty || !info->xmit_buf)
-               return 0;
-
-       save_flags(flags);
-       while (1) {
-               cli();          
-               c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-                                  SERIAL_XMIT_SIZE - info->xmit_head));
-               if (c <= 0)
-                       break;
-
-               if (from_user) {
-                       down(&tmp_buf_sem);
-                       memcpy_fromfs(tmp_buf, buf, c);
-                       c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-                                      SERIAL_XMIT_SIZE - info->xmit_head));
-                       memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
-                       up(&tmp_buf_sem);
-               } else
-                       memcpy(info->xmit_buf + info->xmit_head, buf, c);
-               info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
-               info->xmit_cnt += c;
-               restore_flags(flags);
-               buf += c;
-               count -= c;
-               total += c;
-       }
-       if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
-           && !info->tx_active)
-               transmit_chars(info);
-       restore_flags(flags);
-       return total;
-}
-#endif
-
-/*
- * ------------------------------------------------------------
- * rs_throttle()
- * 
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- * ------------------------------------------------------------
- */
-static void SCC_throttle(struct m68k_async_struct *info, int status)
-{
-       unsigned long   flags;
-
-       save_flags(flags); 
-       cli();
-
-       if (status) {
-               /*
-                * Here we want to turn off the RTS line.  On Macintoshes,
-                * we only get the DTR line, which goes to both DTR and
-                * RTS on the modem.  RTS doesn't go out to the serial
-                * port socket.  So you should make sure your modem is
-                * set to ignore DTR if you're using CRTSCTS.
-                */
-               info->private->curregs[5] &= ~(DTR | RTS);
-               info->private->pendregs[5] &= ~(DTR | RTS);
-               write_zsreg(info->private->zs_channel, 5, 
-                           info->private->curregs[5]);
-       } else {
-               /* Assert RTS and DTR lines */
-               info->private->curregs[5] |= DTR | RTS;
-               info->private->pendregs[5] |= DTR | RTS;
-               write_zsreg(info->private->zs_channel, 5, 
-                           info->private->curregs[5]);
-       }
-
-       restore_flags(flags);
-
-}
-
-/*
- * ------------------------------------------------------------
- * rs_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-static void SCC_get_serial_info(struct m68k_async_struct * info,
-                          struct serial_struct * retinfo)
-{
-       retinfo->baud_base = info->baud_base;
-       retinfo->custom_divisor = info->custom_divisor;
-}
-
-/* FIXME: set_serial_info needs check_custom_divisor !!! */
-
-/*
- * get_lsr_info - get line status register info
- *
- * Purpose: Let user call ioctl() to get info when the UART physically
- *         is emptied.  On bus types like RS485, the transmitter must
- *         release the bus after transmitting. This must be done when
- *         the transmit shift register is empty, not be done when the
- *         transmit holding register is empty.  This functionality
- *         allows an RS485 driver to be written in user space. 
- */
-static int SCC_get_lsr_info(struct m68k_async_struct * info, unsigned int *value)
-{
-       unsigned char status;
-
-       cli();
-       status = read_zsreg(info->private->zs_channel, 0);
-       sti();
-       return status;
-}
-
-static unsigned int SCC_get_modem_info(struct m68k_async_struct *info)
-{
-       unsigned char control, status;
-       unsigned int result;
-
-       cli();
-       control = info->private->curregs[5];
-       status = read_zsreg(info->private->zs_channel, 0);
-       sti();
-       result =  ((control & RTS) ? TIOCM_RTS: 0)
-               | ((control & DTR) ? TIOCM_DTR: 0)
-               | ((status  & DCD) ? TIOCM_CAR: 0)
-               | ((status  & CTS) ? 0: TIOCM_CTS);
-       return result;
-}
-
-/* FIXME: zs_setdtr was used in rs_open ... */
-
-static int SCC_set_modem_info(struct m68k_async_struct *info, 
-                             int new_dtr, int new_rts)
-{
-       unsigned int bits;
-
-       bits = (new_rts ? RTS: 0) + (new_dtr ? DTR: 0);
-       info->private->curregs[5] = (info->private->curregs[5] & ~(DTR | RTS)) | bits;
-       info->private->pendregs[5] = info->private->curregs[5];
-       write_zsreg(info->private->zs_channel, 5, info->private->curregs[5]);
-       sti();
-       return 0;
-}
-
-/*
- * This routine sends a break character out the serial port.
- */
-static void SCC_set_break(struct m68k_async_struct * info, int break_flag)
-{
-       unsigned long   flags;
-
-       save_flags(flags);
-       cli();
-
-       if (break_flag) {
-               info->private->curregs[5] |= SND_BRK;
-               write_zsreg(info->private->zs_channel, 5, 
-                           info->private->curregs[5]);
-       } else {
-               info->private->curregs[5] &= ~SND_BRK;
-               write_zsreg(info->private->zs_channel, 5, 
-                           info->private->curregs[5]);
-       }
-
-       restore_flags(flags);
-}
-
-/* FIXME: these have to be enabled in rs_ioctl !! */
-
-static int SCC_ioctl(struct tty_struct *tty, struct file * file,
-                   struct m68k_async_struct * info, unsigned int cmd, 
-                   unsigned long arg)
-{
-       int error;
-
-       switch (cmd) {
-               case TIOCSERGETLSR: /* Get line status register */
-                       error = verify_area(VERIFY_WRITE, (void *) arg,
-                               sizeof(unsigned int));
-                       if (error)
-                               return error;
-                       else
-                           return SCC_get_lsr_info(info, (unsigned int *) arg);
-
-               case TIOCSERGSTRUCT:
-                       error = verify_area(VERIFY_WRITE, (void *) arg,
-                                               sizeof(struct m68k_async_struct));
-                       if (error)
-                               return error;
-                       copy_to_user((struct m68k_async_struct *) arg,
-                                     info, sizeof(struct m68k_async_struct));
-                       return 0;
-                       
-               default:
-                       return -ENOIOCTLCMD;
-               }
-       return 0;
-}
-
-static void SCC_stop_receive (struct m68k_async_struct *info)
-{
-       /* disable Rx */
-       info->private->curregs[3] &= ~RxENABLE;
-       info->private->pendregs[3] = info->private->curregs[3];
-       write_zsreg(info->private->zs_channel, 3, info->private->curregs[3]);
-       /* disable Rx interrupts */
-       info->private->curregs[1] &= ~(0x18);   /* disable any rx ints */
-       info->private->pendregs[1] = info->private->curregs[1];
-       write_zsreg(info->private->zs_channel, 1, info->private->curregs[1]);
-       ZS_CLEARFIFO(info->private->zs_channel);
-}
-
-static int SCC_trans_empty (struct m68k_async_struct *info)
-{
-       return  (read_zsreg(info->private->zs_channel, 1) & ALL_SNT) != 0;
-}
-
-/* Finally, routines used to initialize the serial driver. */
-
-#ifdef CONFIG_MAC
-
-/*
- *     Mac: use boot_info data; assume 2 channels
- */
-static void probe_sccs(void)
-{
-       int n;
-
-#define ZS_CONTROL     0x50F04000
-#define ZS_DATA                (ZS_CONTROL+4)
-#define ZS_IRQ         5
-#define ZS_MOVE                -2
-#define ZS_DATA_MOVE   4
-#define ZS_CH_A_FIRST  2
-
-       /* last-ditch fixup for NetBSD booter case */
-       if (mac_bi_data.sccbase == 0)
-               mac_bi_data.sccbase = ZS_CONTROL;
-
-       /* testing: fix up broken 24 bit addresses (ClassicII) */
-       if ((mac_bi_data.sccbase & 0x00FFFFFF) == mac_bi_data.sccbase)
-               mac_bi_data.sccbase |= 0x50000000;
-               
-       if ( !hwreg_present((void *)mac_bi_data.sccbase))
-       {
-               printk(KERN_WARNING "z8530: Serial devices not accessible. Check serial switch.\n");
-               return;
-       }
-
-       for(n=0;n<2;n++)
-       {
-#if 0
-               zs_channels[n].control = (volatile unsigned char *)
-                               ZS_CONTROL+ZS_MOVE*n;
-               zs_channels[n].data = (volatile unsigned char *)ZS_DATA+ZS_MOVE*n;
-#else
-               zs_channels[n].control = (volatile unsigned char *) /* 2, 0 */
-                       (mac_bi_data.sccbase+ZS_CH_A_FIRST)+ZS_MOVE*n;
-               zs_channels[n].data = (volatile unsigned char *) /* 6, 4 */
-                       (mac_bi_data.sccbase+ZS_CH_A_FIRST+ZS_DATA_MOVE)+ZS_MOVE*n;
-#endif
-               zs_soft[n].private = &zs_soft_private[n];
-               zs_soft[n].private->zs_channel = &zs_channels[n];
-               zs_soft[n].irq = IRQ4;
-#if 0          
-               if (request_irq(ch->intrs[0], rs_interrupt, 0,
-                               "SCC", &zs_soft[n]))
-                       panic("macserial: can't get irq %d",
-                             ch->intrs[0]);
-#endif
-               if (n & 1)
-                       zs_soft[n].private->zs_chan_a = &zs_channels[n-1];
-               else
-                       zs_soft[n].private->zs_chan_a = &zs_channels[n];
-       }
-
-       zs_channels_found=2;
-}
-
-#else
-
-/*
- *     PowerMAC - query the PROM
- */
-static void show_serial_version(void)
-{
-       printk("PowerMac Z8530 serial driver version 1.00\n");
-}
-
-/* Ask the PROM how many Z8530s we have and initialize their zs_channels */
-static void
-probe_sccs()
-{
-       struct device_node *dev, *ch;
-       struct m68k_async_struct **pp;
-       int n;
-
-       n = 0;
-       pp = &zs_chain;
-       for (dev = find_devices("escc"); dev != 0; dev = dev->next) {
-               if (n >= NUM_CHANNELS) {
-                       printk("Sorry, can't use %s: no more channels\n",
-                              dev->full_name);
-                       continue;
-               }
-               for (ch = dev->child; ch != 0; ch = ch->sibling) {
-                       if (ch->n_addrs < 1 || ch ->n_intrs < 1) {
-                               printk("Can't use %s: %d addrs %d intrs\n",
-                                     ch->full_name, ch->n_addrs, ch->n_intrs);
-                               continue;
-                       }
-                       zs_channels[n].control = (volatile unsigned char *)
-                               ch->addrs[0].address;
-                       zs_channels[n].data = zs_channels[n].control
-                               + ch->addrs[0].size / 2;
-                       zs_soft[n].private = &zs_soft_private[n];
-                       zs_soft[n].private->zs_channel = &zs_channels[n];
-                       zs_soft[n].irq = ch->intrs[0];
-                       if (request_irq(ch->intrs[0], mac_SCC_interrupt, 0,
-                                       "SCC", &zs_soft[n]))
-                               panic("macserial: can't get irq %d",
-                                     ch->intrs[0]);
-                       /* XXX this assumes the prom puts chan A before B */
-                       if (n & 1)
-                               zs_soft[n].private->zs_chan_a = &zs_channels[n-1];
-                       else
-                               zs_soft[n].private->zs_chan_a = &zs_channels[n];
-
-                       *pp = &zs_soft[n];
-                       pp = &zs_soft[n].private->zs_next;
-                       ++n;
-               }
-       }
-       *pp = 0;
-       zs_channels_found = n;
-}
-
-#endif
-
-extern void register_console(void (*proc)(const char *));
-
-static inline void
-rs_cons_check(struct m68k_async_struct *ss, int channel)
-{
-       int i, o, io;
-       static int consout_registered = 0;
-       static int msg_printed = 0;
-
-       i = o = io = 0;
-
-       /* Is this one of the serial console lines? */
-       if ((zs_cons_chanout != channel) &&
-           (zs_cons_chanin != channel))
-               return;
-       zs_conschan = ss->private->zs_channel;
-       zs_consinfo = ss;
-
-       /* Register the console output putchar, if necessary */
-       if (zs_cons_chanout == channel) {
-               o = 1;
-               /* double whee.. */
-               if (!consout_registered) {
-                       register_console(zs_console_print);
-                       consout_registered = 1;
-               }
-       }
-
-       if (zs_cons_chanin == channel) {
-               i = 1;
-       }
-       if (o && i)
-               io = 1;
-       if (ss->private->zs_baud != 9600)
-               panic("Console baud rate weirdness");
-
-       /* Set flag variable for this port so that it cannot be
-        * opened for other uses by accident.
-        */
-       ss->private->is_cons = 1;
-
-       if (io) {
-               if(!msg_printed) {
-                       printk("zs%d: console I/O\n", ((channel>>1)&1));
-                       msg_printed = 1;
-               }
-       } else {
-               printk("zs%d: console %s\n", ((channel>>1)&1),
-                      (i==1 ? "input" : (o==1 ? "output" : "WEIRD")));
-       }
-
-       /* FIXME : register interrupt here??? */
-}
-
-volatile int test_done;
-
-/* rs_init inits the driver */
-int mac_SCC_init(void)
-{
-       int channel, line, nr = 0;
-       unsigned long flags;
-       struct serial_struct req;
-
-       printk("Mac68K Z8530 serial driver version 1.01\n");
-
-       /* SCC present at all? */
-       if (!MACH_IS_MAC)
-               return( -ENODEV );
-
-       if (zs_chain == 0)
-               probe_sccs();
-
-       save_flags(flags);
-       cli();
-
-       /* 
-        * FIXME: init of rs_table entry and register_serial now done,
-        * but possible clash of zs_soft[channel] and rs_table[channel]!!
-        * zs_soft initialized in probe_sccs(), some settings copied to
-        * info = &rs_table[channel], which is used by the mid-level code.
-        * The info->private part is shared among both!
-        */
-
-       for (channel = 0; channel < zs_channels_found; ++channel) {
-               req.line = channel;
-               req.type = SER_SCC_MAC;
-               req.port = (int) zs_soft[channel].private->zs_channel->control;
-
-               if ((line = register_serial( &req )) >= 0) {
-                       SCC_init_port( &rs_table[line], req.type, line );
-                       ++nr;
-               }
-               else
-                       printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
-       }
-
-       restore_flags(flags);
-
-       return( nr > 0 ? 0 : -ENODEV );
-}
-
-/* Hooks for running a serial console.  con_init() calls this if the
- * console is being run over one of the serial ports.
- * 'channel' is decoded as 0=modem 1=printer, 'chip' is ignored.
- */
-void
-rs_cons_hook(int chip, int out, int channel)
-{
-       if (zs_chain == 0)
-               probe_sccs();
-       zs_soft[channel].private->clk_divisor = 16;
-       zs_soft[channel].private->zs_baud = get_zsbaud(&zs_soft[channel]);
-       rs_cons_check(&zs_soft[channel], channel);
-       if (out)
-               zs_cons_chanout = channel;
-       else
-               zs_cons_chanin = channel;
-
-       /* FIXME : register interrupt here??? */
-}
-
-/* This is called at boot time to prime the kgdb serial debugging
- * serial line.  The 'tty_num' argument is 0 for /dev/ttyS0 and 1
- * for /dev/ttyS1 which is determined in setup_arch() from the
- * boot command line flags.
- */
-void
-rs_kgdb_hook(int tty_num)
-{
-       if (zs_chain == 0)
-               probe_sccs();
-       zs_kgdbchan = zs_soft[tty_num].private->zs_channel;
-       zs_soft[tty_num].private->clk_divisor = 16;
-       zs_soft[tty_num].private->zs_baud = get_zsbaud(&zs_soft[tty_num]);
-       zs_soft[tty_num].private->kgdb_channel = 1;     /* This runs kgdb */
-       zs_soft[tty_num ^ 1].private->kgdb_channel = 0; /* This does not */
-       /* Turn on transmitter/receiver at 8-bits/char */
-       kgdb_chaninit(&zs_soft[tty_num], 0, 9600);
-       ZS_CLEARERR(zs_kgdbchan);
-       ZS_CLEARFIFO(zs_kgdbchan);
-
-       /* FIXME : register interrupt here??? */
-}
-
diff --git a/drivers/char/mac_SCC.h b/drivers/char/mac_SCC.h
deleted file mode 100644 (file)
index 5e903e1..0000000
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * macserial.h: Definitions for the Macintosh Z8530 serial driver.
- *
- * Adapted from drivers/sbus/char/sunserial.h by Paul Mackerras.
- *
- * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- */
-#ifndef _MAC_SCC_H
-#define _MAC_SCC_H
-
-/*
- * For the close wait times, 0 means wait forever for serial port to
- * flush its output.  65535 means don't wait at all.
- */
-#define ZILOG_CLOSING_WAIT_INF 0
-#define ZILOG_CLOSING_WAIT_NONE        65535
-
-/*
- * Definitions for ZILOG_struct (and serial_struct) flags field
- */
-#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes 
-                                  on the callout port */
-#define ZILOG_FOURPORT  0x0002 /* Set OU1, OUT2 per AST Fourport settings */
-#define ZILOG_SAK      0x0004  /* Secure Attention Key (Orange book) */
-#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
-
-#define ZILOG_SPD_MASK 0x0030
-#define ZILOG_SPD_HI   0x0010  /* Use 56000 instead of 38400 bps */
-
-#define ZILOG_SPD_VHI  0x0020  /* Use 115200 instead of 38400 bps */
-#define ZILOG_SPD_CUST 0x0030  /* Use user-specified divisor */
-
-#define ZILOG_SKIP_TEST        0x0040 /* Skip UART test during autoconfiguration */
-#define ZILOG_AUTO_IRQ  0x0080 /* Do automatic IRQ during autoconfiguration */
-#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
-#define ZILOG_PGRP_LOCKOUT    0x0200 /* Lock out cua opens based on pgrp */
-#define ZILOG_CALLOUT_NOHUP   0x0400 /* Don't do hangups for cua device */
-
-#define ZILOG_FLAGS    0x0FFF  /* Possible legal ZILOG flags */
-#define ZILOG_USR_MASK 0x0430  /* Legal flags that non-privileged
-                                * users can set or reset */
-
-/* Internal flags used only by kernel/chr_drv/serial.c */
-#define ZILOG_INITIALIZED      0x80000000 /* Serial port was initialized */
-#define ZILOG_CALLOUT_ACTIVE   0x40000000 /* Call out device is active */
-#define ZILOG_NORMAL_ACTIVE    0x20000000 /* Normal device is active */
-#define ZILOG_BOOT_AUTOCONF    0x10000000 /* Autoconfigure port on bootup */
-#define ZILOG_CLOSING          0x08000000 /* Serial port is closing */
-#define ZILOG_CTS_FLOW         0x04000000 /* Do CTS flow control */
-#define ZILOG_CHECK_CD         0x02000000 /* i.e., CLOCAL */
-
-/* Software state per channel */
-
-#ifdef __KERNEL__
-/*
- * This is our internal structure for each serial port's state.
- * 
- * Many fields are paralleled by the structure used by the serial_struct
- * structure.
- *
- * For definitions of the flags field, see tty.h
- */
-
-/* MSch: gone to <asm/serial.h> */
-
-#define SERIAL_MAGIC 0x5301
-
-/*
- * The size of the serial xmit buffer is 1 page, or 4096 bytes
- */
-#define SERIAL_XMIT_SIZE 4096
-
-/*
- * Events are used to schedule things to happen at timer-interrupt
- * time, instead of at rs interrupt time.
- */
-#define RS_EVENT_WRITE_WAKEUP  0
-
-#endif /* __KERNEL__ */
-
-/* Conversion routines to/from brg time constants from/to bits
- * per second.
- */
-#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
-#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
-
-/* The Zilog register set */
-
-#define        FLAG    0x7e
-
-/* Write Register 0 */
-#define        R0      0               /* Register selects */
-#define        R1      1
-#define        R2      2
-#define        R3      3
-#define        R4      4
-#define        R5      5
-#define        R6      6
-#define        R7      7
-#define        R8      8
-#define        R9      9
-#define        R10     10
-#define        R11     11
-#define        R12     12
-#define        R13     13
-#define        R14     14
-#define        R15     15
-
-#define        NULLCODE        0       /* Null Code */
-#define        POINT_HIGH      0x8     /* Select upper half of registers */
-#define        RES_EXT_INT     0x10    /* Reset Ext. Status Interrupts */
-#define        SEND_ABORT      0x18    /* HDLC Abort */
-#define        RES_RxINT_FC    0x20    /* Reset RxINT on First Character */
-#define        RES_Tx_P        0x28    /* Reset TxINT Pending */
-#define        ERR_RES         0x30    /* Error Reset */
-#define        RES_H_IUS       0x38    /* Reset highest IUS */
-
-#define        RES_Rx_CRC      0x40    /* Reset Rx CRC Checker */
-#define        RES_Tx_CRC      0x80    /* Reset Tx CRC Checker */
-#define        RES_EOM_L       0xC0    /* Reset EOM latch */
-
-/* Write Register 1 */
-
-#define        EXT_INT_ENAB    0x1     /* Ext Int Enable */
-#define        TxINT_ENAB      0x2     /* Tx Int Enable */
-#define        PAR_SPEC        0x4     /* Parity is special condition */
-
-#define        RxINT_DISAB     0       /* Rx Int Disable */
-#define        RxINT_FCERR     0x8     /* Rx Int on First Character Only or Error */
-#define        INT_ALL_Rx      0x10    /* Int on all Rx Characters or error */
-#define        INT_ERR_Rx      0x18    /* Int on error only */
-
-#define        WT_RDY_RT       0x20    /* Wait/Ready on R/T */
-#define        WT_FN_RDYFN     0x40    /* Wait/FN/Ready FN */
-#define        WT_RDY_ENAB     0x80    /* Wait/Ready Enable */
-
-/* Write Register #2 (Interrupt Vector) */
-
-/* Write Register 3 */
-
-#define        RxENABLE        0x1     /* Rx Enable */
-#define        SYNC_L_INH      0x2     /* Sync Character Load Inhibit */
-#define        ADD_SM          0x4     /* Address Search Mode (SDLC) */
-#define        RxCRC_ENAB      0x8     /* Rx CRC Enable */
-#define        ENT_HM          0x10    /* Enter Hunt Mode */
-#define        AUTO_ENAB       0x20    /* Auto Enables */
-#define        Rx5             0x0     /* Rx 5 Bits/Character */
-#define        Rx7             0x40    /* Rx 7 Bits/Character */
-#define        Rx6             0x80    /* Rx 6 Bits/Character */
-#define        Rx8             0xc0    /* Rx 8 Bits/Character */
-#define RxNBITS_MASK   0xc0
-
-/* Write Register 4 */
-
-#define        PAR_ENA         0x1     /* Parity Enable */
-#define        PAR_EVEN        0x2     /* Parity Even/Odd* */
-
-#define        SYNC_ENAB       0       /* Sync Modes Enable */
-#define        SB1             0x4     /* 1 stop bit/char */
-#define        SB15            0x8     /* 1.5 stop bits/char */
-#define        SB2             0xc     /* 2 stop bits/char */
-#define SB_MASK                0xc
-
-#define        MONSYNC         0       /* 8 Bit Sync character */
-#define        BISYNC          0x10    /* 16 bit sync character */
-#define        SDLC            0x20    /* SDLC Mode (01111110 Sync Flag) */
-#define        EXTSYNC         0x30    /* External Sync Mode */
-
-#define        X1CLK           0x0     /* x1 clock mode */
-#define        X16CLK          0x40    /* x16 clock mode */
-#define        X32CLK          0x80    /* x32 clock mode */
-#define        X64CLK          0xC0    /* x64 clock mode */
-#define XCLK_MASK      0xC0
-
-/* Write Register 5 */
-
-#define        TxCRC_ENAB      0x1     /* Tx CRC Enable */
-#define        RTS             0x2     /* RTS */
-#define        SDLC_CRC        0x4     /* SDLC/CRC-16 */
-#define        TxENAB          0x8     /* Tx Enable */
-#define        SND_BRK         0x10    /* Send Break */
-#define        Tx5             0x0     /* Tx 5 bits (or less)/character */
-#define        Tx7             0x20    /* Tx 7 bits/character */
-#define        Tx6             0x40    /* Tx 6 bits/character */
-#define        Tx8             0x60    /* Tx 8 bits/character */
-#define TxNBITS_MASK   0x60
-#define        DTR             0x80    /* DTR */
-
-/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
-
-/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
-
-/* Write Register 8 (transmit buffer) */
-
-/* Write Register 9 (Master interrupt control) */
-#define        VIS     1       /* Vector Includes Status */
-#define        NV      2       /* No Vector */
-#define        DLC     4       /* Disable Lower Chain */
-#define        MIE     8       /* Master Interrupt Enable */
-#define        STATHI  0x10    /* Status high */
-#define        NORESET 0       /* No reset on write to R9 */
-#define        CHRB    0x40    /* Reset channel B */
-#define        CHRA    0x80    /* Reset channel A */
-#define        FHWRES  0xc0    /* Force hardware reset */
-
-/* Write Register 10 (misc control bits) */
-#define        BIT6    1       /* 6 bit/8bit sync */
-#define        LOOPMODE 2      /* SDLC Loop mode */
-#define        ABUNDER 4       /* Abort/flag on SDLC xmit underrun */
-#define        MARKIDLE 8      /* Mark/flag on idle */
-#define        GAOP    0x10    /* Go active on poll */
-#define        NRZ     0       /* NRZ mode */
-#define        NRZI    0x20    /* NRZI mode */
-#define        FM1     0x40    /* FM1 (transition = 1) */
-#define        FM0     0x60    /* FM0 (transition = 0) */
-#define        CRCPS   0x80    /* CRC Preset I/O */
-
-/* Write Register 11 (Clock Mode control) */
-#define        TRxCXT  0       /* TRxC = Xtal output */
-#define        TRxCTC  1       /* TRxC = Transmit clock */
-#define        TRxCBR  2       /* TRxC = BR Generator Output */
-#define        TRxCDP  3       /* TRxC = DPLL output */
-#define        TRxCOI  4       /* TRxC O/I */
-#define        TCRTxCP 0       /* Transmit clock = RTxC pin */
-#define        TCTRxCP 8       /* Transmit clock = TRxC pin */
-#define        TCBR    0x10    /* Transmit clock = BR Generator output */
-#define        TCDPLL  0x18    /* Transmit clock = DPLL output */
-#define        RCRTxCP 0       /* Receive clock = RTxC pin */
-#define        RCTRxCP 0x20    /* Receive clock = TRxC pin */
-#define        RCBR    0x40    /* Receive clock = BR Generator output */
-#define        RCDPLL  0x60    /* Receive clock = DPLL output */
-#define        RTxCX   0x80    /* RTxC Xtal/No Xtal */
-
-/* Write Register 12 (lower byte of baud rate generator time constant) */
-
-/* Write Register 13 (upper byte of baud rate generator time constant) */
-
-/* Write Register 14 (Misc control bits) */
-#define        BRENABL 1       /* Baud rate generator enable */
-#define        BRSRC   2       /* Baud rate generator source */
-#define        DTRREQ  4       /* DTR/Request function */
-#define        AUTOECHO 8      /* Auto Echo */
-#define        LOOPBAK 0x10    /* Local loopback */
-#define        SEARCH  0x20    /* Enter search mode */
-#define        RMC     0x40    /* Reset missing clock */
-#define        DISDPLL 0x60    /* Disable DPLL */
-#define        SSBR    0x80    /* Set DPLL source = BR generator */
-#define        SSRTxC  0xa0    /* Set DPLL source = RTxC */
-#define        SFMM    0xc0    /* Set FM mode */
-#define        SNRZI   0xe0    /* Set NRZI mode */
-
-/* Write Register 15 (external/status interrupt control) */
-#define        ZCIE    2       /* Zero count IE */
-#define        DCDIE   8       /* DCD IE */
-#define        SYNCIE  0x10    /* Sync/hunt IE */
-#define        CTSIE   0x20    /* CTS IE */
-#define        TxUIE   0x40    /* Tx Underrun/EOM IE */
-#define        BRKIE   0x80    /* Break/Abort IE */
-
-
-/* Read Register 0 */
-#define        Rx_CH_AV        0x1     /* Rx Character Available */
-#define        ZCOUNT          0x2     /* Zero count */
-#define        Tx_BUF_EMP      0x4     /* Tx Buffer empty */
-#define        DCD             0x8     /* DCD */
-#define        SYNC_HUNT       0x10    /* Sync/hunt */
-#define        CTS             0x20    /* CTS */
-#define        TxEOM           0x40    /* Tx underrun */
-#define        BRK_ABRT        0x80    /* Break/Abort */
-
-/* Read Register 1 */
-#define        ALL_SNT         0x1     /* All sent */
-/* Residue Data for 8 Rx bits/char programmed */
-#define        RES3            0x8     /* 0/3 */
-#define        RES4            0x4     /* 0/4 */
-#define        RES5            0xc     /* 0/5 */
-#define        RES6            0x2     /* 0/6 */
-#define        RES7            0xa     /* 0/7 */
-#define        RES8            0x6     /* 0/8 */
-#define        RES18           0xe     /* 1/8 */
-#define        RES28           0x0     /* 2/8 */
-/* Special Rx Condition Interrupts */
-#define        PAR_ERR         0x10    /* Parity error */
-#define        Rx_OVR          0x20    /* Rx Overrun Error */
-#define        FRM_ERR         0x40    /* CRC/Framing Error */
-#define        END_FR          0x80    /* End of Frame (SDLC) */
-
-/* Read Register 2 (channel b only) - Interrupt vector */
-
-/* Read Register 3 (interrupt pending register) ch a only */
-#define        CHBEXT  0x1             /* Channel B Ext/Stat IP */
-#define        CHBTxIP 0x2             /* Channel B Tx IP */
-#define        CHBRxIP 0x4             /* Channel B Rx IP */
-#define        CHAEXT  0x8             /* Channel A Ext/Stat IP */
-#define        CHATxIP 0x10            /* Channel A Tx IP */
-#define        CHARxIP 0x20            /* Channel A Rx IP */
-
-/* Read Register 8 (receive data register) */
-
-/* Read Register 10  (misc status bits) */
-#define        ONLOOP  2               /* On loop */
-#define        LOOPSEND 0x10           /* Loop sending */
-#define        CLK2MIS 0x40            /* Two clocks missing */
-#define        CLK1MIS 0x80            /* One clock missing */
-
-/* Read Register 12 (lower byte of baud rate generator constant) */
-
-/* Read Register 13 (upper byte of baud rate generator constant) */
-
-/* Read Register 15 (value of WR 15) */
-
-/* Misc macros */
-#define ZS_CLEARERR(channel)    (write_zsreg(channel, 0, ERR_RES))
-#define ZS_CLEARFIFO(channel)   do { volatile unsigned char garbage; \
-                                    garbage = read_zsdata(channel); \
-                                    garbage = read_zsdata(channel); \
-                                    garbage = read_zsdata(channel); \
-                               } while(0)
-
-#endif /* !(_MAC_SCC_H) */
index 14f1ac1819317b8078903502d720e4c56e7361d1..c9a40a3c196499788f15d3b144f145beef0244a4 100644 (file)
@@ -68,6 +68,10 @@ ifdef CONFIG_ADB_PMU
   L_OBJS += via-pmu.o
 endif
 
+ifdef CONFIG_ADB_PMU68K
+  L_OBJS += via-pmu68k.o
+endif
+
 ifdef CONFIG_ADB_MACIO
   L_OBJS += macio-adb.o
 endif
diff --git a/drivers/macintosh/adb-iop.c b/drivers/macintosh/adb-iop.c
new file mode 100644 (file)
index 0000000..3c3db0a
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * I/O Processor (IOP) ADB Driver
+ * Written and (C) 1999 by Joshua M. Thompson (funaho@jurai.org)
+ * Based on via-cuda.c by Paul Mackerras.
+ *
+ * 1999-07-01 (jmt) - First implementation for new driver architecture.
+ *
+ * 1999-07-31 (jmt) - First working version.
+ *
+ * TODO:
+ *
+ * o Implement SRQ handling.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+
+#include <asm/bootinfo.h> 
+#include <asm/macintosh.h> 
+#include <asm/macints.h> 
+#include <asm/mac_iop.h>
+#include <asm/mac_oss.h>
+#include <asm/adb_iop.h>
+
+#include <linux/adb.h> 
+
+/*#define DEBUG_ADB_IOP*/
+
+extern void iop_ism_irq(int, void *, struct pt_regs *);
+
+static struct adb_request *current_req;
+static struct adb_request *last_req;
+#if 0
+static unsigned char reply_buff[16];
+static unsigned char *reply_ptr;
+#endif
+
+static enum adb_iop_state {
+    idle,
+    sending,
+    awaiting_reply
+} adb_iop_state;
+
+static void adb_iop_start(void);
+static int adb_iop_probe(void);
+static int adb_iop_init(void);
+static int adb_iop_send_request(struct adb_request *, int);
+static int adb_iop_write(struct adb_request *);
+static int adb_iop_autopoll(int);
+static void adb_iop_poll(void);
+static int adb_iop_reset_bus(void);
+
+struct adb_driver adb_iop_driver = {
+       "ISM IOP",
+       adb_iop_probe,
+       adb_iop_init,
+       adb_iop_send_request,
+       adb_iop_autopoll,
+       adb_iop_poll,
+       adb_iop_reset_bus
+};
+
+static void adb_iop_end_req(struct adb_request *req, int state)
+{
+       req->complete = 1;
+       current_req = req->next;
+       if (req->done) (*req->done)(req);
+       adb_iop_state = state;
+}
+
+/*
+ * Completion routine for ADB commands sent to the IOP.
+ *
+ * This will be called when a packet has been successfully sent.
+ */
+
+static void adb_iop_complete(struct iop_msg *msg, struct pt_regs *regs)
+{
+       struct adb_request *req;
+       uint flags;
+
+       save_flags(flags);
+       cli();
+
+       req = current_req;
+       if ((adb_iop_state == sending) && req && req->reply_expected) {
+               adb_iop_state = awaiting_reply;
+       }
+
+       restore_flags(flags);
+}
+
+/*
+ * Listen for ADB messages from the IOP.
+ *
+ * This will be called when unsolicited messages (usually replies to TALK
+ * commands or autopoll packets) are received.
+ */
+
+static void adb_iop_listen(struct iop_msg *msg, struct pt_regs *regs)
+{
+       struct adb_iopmsg *amsg = (struct adb_iopmsg *) msg->message;
+       struct adb_request *req;
+       uint flags;
+
+       save_flags(flags);
+       cli();
+
+       req = current_req;
+
+#ifdef DEBUG_ADB_IOP
+       printk("adb_iop_listen: rcvd packet, %d bytes: %02X %02X",
+               (uint) amsg->count + 2, (uint) amsg->flags, (uint) amsg->cmd);
+       i = 0;
+       while (i < amsg->count) {
+               printk(" %02X", (uint) amsg->data[i++]);
+       }
+       printk("\n");
+#endif
+
+       /* Handle a timeout. Timeout packets seem to occur even after */
+       /* we've gotten a valid reply to a TALK, so I'm assuming that */
+       /* a "timeout" is actually more like an "end-of-data" signal. */
+       /* We need to send back a timeout packet to the IOP to shut   */
+       /* it up, plus complete the current request, if any.          */
+
+       if (amsg->flags & ADB_IOP_TIMEOUT) {
+               msg->reply[0] = ADB_IOP_TIMEOUT | ADB_IOP_AUTOPOLL;
+               msg->reply[1] = 0;
+               msg->reply[2] = 0;
+               if (req && (adb_iop_state != idle)) {
+                       adb_iop_end_req(req, idle);
+               }
+       } else {
+               /* TODO: is it possible for more tha one chunk of data  */
+               /*       to arrive before the timeout? If so we need to */
+               /*       use reply_ptr here like the other drivers do.  */
+               if ((adb_iop_state == awaiting_reply) &&
+                   (amsg->flags & ADB_IOP_EXPLICIT)) {
+                       req->reply_len = amsg->count + 1;
+                       memcpy(req->reply, &amsg->cmd, req->reply_len);
+               } else {
+                       adb_input(&amsg->cmd, amsg->count + 1, regs,
+                                 amsg->flags & ADB_IOP_AUTOPOLL);
+               }
+               memcpy(msg->reply, msg->message, IOP_MSG_LEN);
+       }
+       iop_complete_message(msg);
+       restore_flags(flags);
+}
+
+/*
+ * Start sending an ADB packet, IOP style
+ *
+ * There isn't much to do other than hand the packet over to the IOP
+ * after encapsulating it in an adb_iopmsg.
+ */
+
+static void adb_iop_start(void)
+{
+       unsigned long flags;
+       struct adb_request *req;
+       struct adb_iopmsg amsg;
+
+       /* get the packet to send */
+       req = current_req;
+       if (!req) return;
+
+       save_flags(flags);
+       cli();
+
+#ifdef DEBUG_ADB_IOP
+       printk("adb_iop_start: sending packet, %d bytes:", req->nbytes);
+       for (i = 0 ; i < req->nbytes ; i++)
+               printk(" %02X", (uint) req->data[i]);
+       printk("\n");
+#endif
+
+       /* The IOP takes MacII-style packets, so */
+       /* strip the initial ADB_PACKET byte.    */
+
+       amsg.flags = ADB_IOP_EXPLICIT;
+       amsg.count = req->nbytes - 2;
+
+       /* amsg.data immediately follows amsg.cmd, effectively making */
+       /* amsg.cmd a pointer to the beginning of a full ADB packet.  */
+       memcpy(&amsg.cmd, req->data + 1, req->nbytes - 1);
+
+       req->sent = 1;
+       adb_iop_state = sending;
+       restore_flags(flags);
+
+       /* Now send it. The IOP manager will call adb_iop_complete */
+       /* when the packet has been sent.                          */
+
+       iop_send_message(ADB_IOP, ADB_CHAN, req,
+                        sizeof(amsg), (__u8 *) &amsg, adb_iop_complete);
+}
+
+int adb_iop_probe(void)
+{
+       if (!iop_ism_present) return -ENODEV;
+       return 0;
+}
+
+int adb_iop_init(void)
+{
+       printk("adb: IOP ISM driver v0.4 for Unified ADB.\n");
+       iop_listen(ADB_IOP, ADB_CHAN, adb_iop_listen, "ADB");
+       return 0;
+}
+
+int adb_iop_send_request(struct adb_request *req, int sync)
+{
+       int err;
+
+       err = adb_iop_write(req);
+       if (err) return err;
+
+       if (sync) {
+               while (!req->complete) adb_iop_poll();
+       }
+       return 0;
+}
+
+static int adb_iop_write(struct adb_request *req)
+{
+       unsigned long flags;
+
+       if ((req->nbytes < 2) || (req->data[0] != ADB_PACKET)) {
+               req->complete = 1;
+               return -EINVAL;
+       }
+
+       save_flags(flags);
+       cli();
+
+       req->next = 0;
+       req->sent = 0;
+       req->complete = 0;
+       req->reply_len = 0;
+
+       if (current_req != 0) {
+               last_req->next = req;
+               last_req = req;
+       } else {
+               current_req = req;
+               last_req = req;
+       }
+
+       restore_flags(flags);
+       if (adb_iop_state == idle) adb_iop_start();
+       return 0;
+}
+
+int adb_iop_autopoll(int devs)
+{
+       /* TODO: how do we enable/disable autopoll? */
+       return 0;
+}
+
+void adb_iop_poll(void)
+{
+       if (adb_iop_state == idle) adb_iop_start();
+       iop_ism_irq(0, (void *) ADB_IOP, NULL);
+}
+
+int adb_iop_reset_bus(void)
+{
+       struct adb_request req;
+
+       req.reply_expected = 0;
+       req.nbytes = 2;
+       req.data[0] = ADB_PACKET;
+       req.data[1] = 0; /* RESET */
+       adb_iop_write(&req);
+       while (!req.complete) adb_iop_poll();
+       return 0;
+}
diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c
new file mode 100644 (file)
index 0000000..f4670f1
--- /dev/null
@@ -0,0 +1,679 @@
+/*
+ * Device driver for the via ADB on (many) Mac II-class machines
+ *
+ * Based on the original ADB keyboard handler Copyright (c) 1997 Alan Cox
+ * Also derived from code Copyright (C) 1996 Paul Mackerras.
+ *
+ * With various updates provided over the years by Michael Schmitz,
+ * Guideo Koerber and others.
+ *
+ * Rewrite for Unified ADB by Joshua M. Thompson (funaho@jurai.org)
+ *
+ * 1999-08-02 (jmt) - Initial rewrite for Unified ADB.
+ */
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/adb.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+#include <asm/mac_via.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/init.h>
+
+static volatile unsigned char *via;
+
+/* VIA registers - spaced 0x200 bytes apart */
+#define RS             0x200           /* skip between registers */
+#define B              0               /* B-side data */
+#define A              RS              /* A-side data */
+#define DIRB           (2*RS)          /* B-side direction (1=output) */
+#define DIRA           (3*RS)          /* A-side direction (1=output) */
+#define T1CL           (4*RS)          /* Timer 1 ctr/latch (low 8 bits) */
+#define T1CH           (5*RS)          /* Timer 1 counter (high 8 bits) */
+#define T1LL           (6*RS)          /* Timer 1 latch (low 8 bits) */
+#define T1LH           (7*RS)          /* Timer 1 latch (high 8 bits) */
+#define T2CL           (8*RS)          /* Timer 2 ctr/latch (low 8 bits) */
+#define T2CH           (9*RS)          /* Timer 2 counter (high 8 bits) */
+#define SR             (10*RS)         /* Shift register */
+#define ACR            (11*RS)         /* Auxiliary control register */
+#define PCR            (12*RS)         /* Peripheral control register */
+#define IFR            (13*RS)         /* Interrupt flag register */
+#define IER            (14*RS)         /* Interrupt enable register */
+#define ANH            (15*RS)         /* A-side data, no handshake */
+
+/* Bits in B data register: all active low */
+#define TREQ           0x08            /* Transfer request (input) */
+#define TACK           0x10            /* Transfer acknowledge (output) */
+#define TIP            0x20            /* Transfer in progress (output) */
+#define ST_MASK                0x30            /* mask for selecting ADB state bits */
+
+/* Bits in ACR */
+#define SR_CTRL                0x1c            /* Shift register control bits */
+#define SR_EXT         0x0c            /* Shift on external clock */
+#define SR_OUT         0x10            /* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET                0x80            /* set bits in IER */
+#define IER_CLR                0               /* clear bits in IER */
+#define SR_INT         0x04            /* Shift register full/empty */
+#define SR_DATA                0x08            /* Shift register data */
+#define SR_CLOCK       0x10            /* Shift register clock */
+
+/* ADB transaction states according to GMHW */
+#define ST_CMD         0x00            /* ADB state: command byte */
+#define ST_EVEN                0x10            /* ADB state: even data byte */
+#define ST_ODD         0x20            /* ADB state: odd data byte */
+#define ST_IDLE                0x30            /* ADB state: idle, nothing to send */
+
+static int  macii_init_via(void);
+static void macii_start(void);
+static void macii_interrupt(int irq, void *arg, struct pt_regs *regs);
+static void macii_retransmit(int);
+static void macii_queue_poll(void);
+
+static int macii_probe(void);
+static int macii_init(void);
+static int macii_send_request(struct adb_request *req, int sync);
+static int macii_write(struct adb_request *req);
+static int macii_autopoll(int devs);
+static void macii_poll(void);
+static int macii_reset_bus(void);
+
+struct adb_driver via_macii_driver = {
+       "Mac II",
+       macii_probe,
+       macii_init,
+       macii_send_request,
+       macii_autopoll,
+       macii_poll,
+       macii_reset_bus
+};
+
+static enum macii_state {
+       idle,
+       sent_first_byte,
+       sending,
+       reading,
+       read_done,
+       awaiting_reply
+} macii_state;
+
+static int need_poll    = 0;
+static int command_byte = 0;
+static int last_reply   = 0;
+static int last_active  = 0;
+
+static struct adb_request *current_req;
+static struct adb_request *last_req;
+static struct adb_request *retry_req;
+static unsigned char reply_buf[16];
+static unsigned char *reply_ptr;
+static int reply_len;
+static int reading_reply;
+static int data_index;
+static int first_byte;
+static int prefix_len;
+static int status = ST_IDLE|TREQ;
+static int last_status;
+static int driver_running = 0;
+
+/* debug level 10 required for ADB logging (should be && debug_adb, ideally) */
+extern int console_loglevel;
+
+/* Check for MacII style ADB */
+static int macii_probe(void)
+{
+       if (macintosh_config->adb_type != MAC_ADB_II) return -ENODEV;
+
+       via = via1;
+
+       printk("adb: Mac II ADB Driver v0.4 for Unified ADB\n");
+       return 0;
+}
+
+/* Initialize the driver */
+int macii_init(void)
+{
+       unsigned long flags;
+       int err;
+       
+       save_flags(flags);
+       cli();
+       
+       err = macii_init_via();
+       if (err) return err;
+
+       err = request_irq(IRQ_MAC_ADB, macii_interrupt, IRQ_FLG_LOCK, "ADB",
+                   macii_interrupt);
+       if (err) return err;
+
+       macii_state = idle;
+       restore_flags(flags);   
+       return 0;
+}
+
+/* initialize the hardware */  
+static int macii_init_via(void)
+{
+       unsigned char x;
+
+       /* Set the lines up. We want TREQ as input TACK|TIP as output */
+       via[DIRB] = (via[DIRB] | TACK | TIP) & ~TREQ;
+
+       /* Set up state: idle */
+       via[B] |= ST_IDLE;
+       last_status = via[B] & (ST_MASK|TREQ);
+
+       /* Shift register on input */
+       via[ACR] = (via[ACR] & ~SR_CTRL) | SR_EXT;
+
+       /* Wipe any pending data and int */
+       x = via[SR];
+
+       return 0;
+}
+
+/* Send an ADB poll (Talk Register 0 command, tagged on the front of the request queue) */
+static void macii_queue_poll(void)
+{
+       static int device = 0;
+       static int in_poll=0;
+       static struct adb_request req;
+       unsigned long flags;
+
+       if (in_poll) printk("macii_queue_poll: double poll!\n");
+
+       in_poll++;
+       if (++device > 15) device = 1;
+
+       adb_request(&req, NULL, ADBREQ_REPLY|ADBREQ_NOSEND, 1,
+                   ADB_READREG(device, 0));
+
+       save_flags(flags);
+       cli();
+
+       req.next = current_req;
+       current_req = &req;
+
+       restore_flags(flags);
+       macii_start();
+       in_poll--;
+}
+
+/* Send an ADB retransmit (Talk, appended to the request queue) */
+static void macii_retransmit(int device)
+{
+       static int in_retransmit = 0;
+       static struct adb_request rt;
+       unsigned long flags;
+
+       if (in_retransmit) printk("macii_retransmit: double retransmit!\n");
+
+       in_retransmit++;
+
+       adb_request(&rt, NULL, ADBREQ_REPLY|ADBREQ_NOSEND, 1,
+                   ADB_READREG(device, 0));
+
+       save_flags(flags);
+       cli();
+
+       if (current_req != NULL) {
+               last_req->next = &rt;
+               last_req = &rt;
+       } else {
+               current_req = &rt;
+               last_req = &rt;
+       }
+
+       if (macii_state == idle) macii_start();
+
+       restore_flags(flags);
+       in_retransmit--;
+}
+
+/* Send an ADB request; if sync, poll out the reply 'till it's done */
+static int macii_send_request(struct adb_request *req, int sync)
+{
+       int i;
+
+       i = macii_write(req);
+       if (i) return i;
+
+       if (sync) {
+               while (!req->complete) macii_poll();
+       }
+       return 0;
+}
+
+/* Send an ADB request */
+static int macii_write(struct adb_request *req)
+{
+       unsigned long flags;
+
+       if (req->nbytes < 2 || req->data[0] != ADB_PACKET) {
+               req->complete = 1;
+               return -EINVAL;
+       }
+
+       req->next = 0;
+       req->sent = 0;
+       req->complete = 0;
+       req->reply_len = 0;
+
+       save_flags(flags); cli();
+
+       if (current_req != NULL) {
+               last_req->next = req;
+               last_req = req;
+       } else {
+               current_req = req;
+               last_req = req;
+               if (macii_state == idle) macii_start();
+       }
+
+       restore_flags(flags);
+       return 0;
+}
+
+/* Start auto-polling */
+static int macii_autopoll(int devs)
+{
+       /* Just ping a random default address */
+       if (!(current_req || retry_req))
+               macii_retransmit( (last_active < 16 && last_active > 0) ? last_active : 3);
+       return 0;
+}
+
+/* Prod the chip without interrupts */
+static void macii_poll(void)
+{
+       unsigned long flags;
+
+       save_flags(flags); cli();
+       if (via[IFR] & SR_INT) macii_interrupt(0, 0, 0);
+       restore_flags(flags);
+}
+
+/* Reset the bus */
+static int macii_reset_bus(void)
+{
+       static struct adb_request req;
+       
+       /* Command = 0, Address = ignored */
+       adb_request(&req, NULL, 0, 1, ADB_BUSRESET);
+
+       return 0;
+}
+
+/* Start sending ADB packet */
+static void macii_start(void)
+{
+       unsigned long flags;
+       struct adb_request *req;
+
+       req = current_req;
+       if (!req) return;
+
+       /* assert macii_state == idle */
+       if (macii_state != idle) {
+               printk("macii_start: called while driver busy (%p %x %x)!\n",
+                       req, macii_state, (uint) via1[B] & (ST_MASK|TREQ));
+               return;
+       }
+
+       save_flags(flags); cli();
+       
+       /* 
+        * IRQ signaled ?? (means ADB controller wants to send, or might 
+        * be end of packet if we were reading)
+        */
+       if ((via[B] & TREQ) == 0) {
+               /*
+                *      FIXME - we need to restart this on a timer
+                *      or a collision at boot hangs us.
+                *      Never set macii_state to idle here, or macii_start 
+                *      won't be called again from send_request!
+                *      (need to re-check other cases ...)
+                */
+               /*
+                * if the interrupt handler set the need_poll
+                * flag, it's hopefully a SRQ poll or re-Talk
+                * so we try to send here anyway
+                */
+               if (!need_poll) {
+                       if (console_loglevel == 10)
+                               printk("macii_start: device busy - retry %p state %d status %x!\n", 
+                                       req, macii_state,
+                                       (uint) via[B] & (ST_MASK|TREQ));
+                       retry_req = req;
+                       /* set ADB status here ? */
+                       restore_flags(flags);
+                       return;
+               } else {
+                       need_poll = 0;
+               }
+       }
+       /*
+        * Another retry pending? (sanity check)
+        */
+       if (retry_req) {
+               retry_req = NULL;
+       }
+
+       /* Now send it. Be careful though, that first byte of the request */
+       /* is actually ADB_PACKET; the real data begins at index 1!       */
+
+       /* store command byte */
+       command_byte = req->data[1];
+       /* Output mode */
+       via[ACR] |= SR_OUT;
+       /* Load data */
+       via[SR] = req->data[1];
+       /* set ADB state to 'command' */
+       via[B] = (via[B] & ~ST_MASK) | ST_CMD;
+
+       macii_state = sent_first_byte;
+       data_index = 2;
+
+       restore_flags(flags);
+}
+
+/*
+ * The notorious ADB interrupt handler - does all of the protocol handling, 
+ * except for starting new send operations. Relies heavily on the ADB 
+ * controller sending and receiving data, thereby generating SR interrupts
+ * for us. This means there has to be always activity on the ADB bus, otherwise
+ * the whole process dies and has to be re-kicked by sending TALK requests ...
+ * CUDA-based Macs seem to solve this with the autopoll option, for MacII-type
+ * ADB the problem isn't solved yet (retransmit of the latest active TALK seems
+ * a good choice; either on timeout or on a timer interrupt).
+ *
+ * The basic ADB state machine was left unchanged from the original MacII code
+ * by Alan Cox, which was based on the CUDA driver for PowerMac. 
+ * The syntax of the ADB status lines seems to be totally different on MacII, 
+ * though. MacII uses the states Command -> Even -> Odd -> Even ->...-> Idle for
+ * sending, and Idle -> Even -> Odd -> Even ->...-> Idle for receiving. Start 
+ * and end of a receive packet are signaled by asserting /IRQ on the interrupt
+ * line. Timeouts are signaled by a sequence of 4 0xFF, with /IRQ asserted on 
+ * every other byte. SRQ is probably signaled by 3 or more 0xFF tacked on the 
+ * end of a packet. (Thanks to Guido Koerber for eavesdropping on the ADB 
+ * protocol with a logic analyzer!!)
+ *
+ * Note: As of 21/10/97, the MacII ADB part works including timeout detection
+ * and retransmit (Talk to the last active device).
+ */
+void macii_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+       int x, adbdir;
+       unsigned long flags;
+       struct adb_request *req;
+
+       last_status = status;
+
+       /* prevent races due to SCSI enabling ints */
+       save_flags(flags); cli();
+
+       if (driver_running) {
+               restore_flags(flags);
+               return;
+       }
+
+       driver_running = 1;
+       
+       status = via[B] & (ST_MASK|TREQ);
+       adbdir = via[ACR] & SR_OUT;
+
+       switch (macii_state) {
+               case idle:
+                       x = via[SR];
+                       first_byte = x;
+                       /* set ADB state = even for first data byte */
+                       via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
+
+                       reply_buf[0] = first_byte; /* was command_byte?? */
+                       reply_ptr = reply_buf + 1;
+                       reply_len = 1;
+                       prefix_len = 1;
+                       reading_reply = 0;
+
+                       macii_state = reading;
+                       break;
+
+               case awaiting_reply:
+                       /* handshake etc. for II ?? */
+                       x = via[SR];
+                       first_byte = x;
+                       /* set ADB state = even for first data byte */
+                       via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
+
+                       current_req->reply[0] = first_byte;
+                       reply_ptr = current_req->reply + 1;
+                       reply_len = 1;
+                       prefix_len = 1;
+                       reading_reply = 1;
+
+                       macii_state = reading;                  
+                       break;
+
+               case sent_first_byte:
+                       req = current_req;
+                       /* maybe we're already done (Talk, or Poll)? */
+                       if (data_index >= req->nbytes) {
+                               /* reset to shift in */
+                               /* If it's a Listen command and we're done, someone's doing weird stuff. */
+                               if (((command_byte & 0x0C) == 0x08)
+                                   && (console_loglevel == 10))
+                                       printk("macii_interrupt: listen command with no data: %x!\n", 
+                                               command_byte);
+                               /* reset to shift in */
+                               via[ACR] &= ~SR_OUT;
+                               x = via[SR];
+                               /* set ADB state idle - might get SRQ */
+                               via[B] = (via[B] & ~ST_MASK) | ST_IDLE;
+
+                               req->sent = 1;
+
+                               if (req->reply_expected) {
+                                       macii_state = awaiting_reply;
+                               } else {
+                                       req->complete = 1;
+                                       current_req = req->next;
+                                       if (req->done) (*req->done)(req);
+                                       macii_state = idle;
+                                       if (current_req || retry_req)
+                                               macii_start();
+                                       else
+                                               macii_retransmit((command_byte & 0xF0) >> 4);
+                               }
+                       } else {
+                               /* SR already set to shift out; send byte */
+                               via[SR] = current_req->data[data_index++];
+                               /* set state to ST_EVEN (first byte was: ST_CMD) */
+                               via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
+                               macii_state = sending;
+                       }
+                       break;
+
+               case sending:
+                       req = current_req;
+                       if (data_index >= req->nbytes) {
+                               /* reset to shift in */
+                               via[ACR] &= ~SR_OUT;
+                               x = via[SR];
+                               /* set ADB state idle - might get SRQ */
+                               via[B] = (via[B] & ~ST_MASK) | ST_IDLE;
+
+                               req->sent = 1;
+
+                               if (req->reply_expected) {
+                                       macii_state = awaiting_reply;
+                               } else {
+                                       req->complete = 1;
+                                       current_req = req->next;
+                                       if (req->done) (*req->done)(req);
+                                       macii_state = idle;
+                                       if (current_req || retry_req)
+                                               macii_start();
+                                       else
+                                               macii_retransmit((command_byte & 0xF0) >> 4);
+                               }
+                       } else {
+                               via[SR] = req->data[data_index++];
+
+                               /* invert state bits, toggle ODD/EVEN */
+                               via[B] ^= ST_MASK;
+                       }
+                       break;
+
+               case reading:
+
+                       /* timeout / SRQ handling for II hw */
+                       if( (first_byte == 0xFF && (reply_len-prefix_len)==2 
+                            && memcmp(reply_ptr-2,"\xFF\xFF",2)==0) || 
+                           ((reply_len-prefix_len)==3 
+                            && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0))
+                       {
+                               /*
+                                * possible timeout (in fact, most probably a 
+                                * timeout, since SRQ can't be signaled without
+                                * transfer on the bus).
+                                * The last three bytes seen were FF, together 
+                                * with the starting byte (in case we started
+                                * on 'idle' or 'awaiting_reply') this probably
+                                * makes four. So this is mostl likely #5!
+                                * The timeout signal is a pattern 1 0 1 0 0..
+                                * on /INT, meaning we missed it :-(
+                                */
+                               x = via[SR];
+                               if (x != 0xFF) printk("macii_interrupt: mistaken timeout/SRQ!\n");
+
+                               if ((status & TREQ) == (last_status & TREQ)) {
+                                       /* Not a timeout. Unsolicited SRQ? weird. */
+                                       /* Terminate the SRQ packet and poll */
+                                       need_poll = 1;
+                               }
+                               /* There's no packet to get, so reply is blank */
+                               via[B] ^= ST_MASK;
+                               reply_ptr -= (reply_len-prefix_len);
+                               reply_len = prefix_len;
+                               macii_state = read_done;
+                               break;
+                       } /* end timeout / SRQ handling for II hw. */
+
+                       if((reply_len-prefix_len)>3
+                               && memcmp(reply_ptr-3,"\xFF\xFF\xFF",3)==0)
+                       {
+                               /* SRQ tacked on data packet */
+                               /* Terminate the packet (SRQ never ends) */
+                               x = via[SR];
+                               macii_state = read_done;
+                               reply_len -= 3;
+                               reply_ptr -= 3;
+                               need_poll = 1;
+                               /* need to continue; next byte not seen else */
+                       } else {
+                               /* Sanity check */
+                               if (reply_len > 15) reply_len = 0;
+                               /* read byte */
+                               x = via[SR];
+                               *reply_ptr = x;
+                               reply_ptr++;
+                               reply_len++;
+                       }
+                       /* The usual handshake ... */
+
+                       /*
+                        * NetBSD hints that the next to last byte 
+                        * is sent with IRQ !! 
+                        * Guido found out it's the last one (0x0),
+                        * but IRQ should be asserted already.
+                        * Problem with timeout detection: First
+                        * transition to /IRQ might be second 
+                        * byte of timeout packet! 
+                        * Timeouts are signaled by 4x FF.
+                        */
+                       if (!(status & TREQ) && (x == 0x00)) { /* != 0xFF */
+                               /* invert state bits, toggle ODD/EVEN */
+                               via[B] ^= ST_MASK;
+
+                               /* adjust packet length */
+                               reply_len--;
+                               reply_ptr--;
+                               macii_state = read_done;
+                       } else {
+                               /* not caught: ST_CMD */
+                               /* required for re-entry 'reading'! */
+                               if ((status & ST_MASK) == ST_IDLE) {
+                                       /* (in)sanity check - set even */
+                                       via[B] = (via[B] & ~ST_MASK) | ST_EVEN;
+                               } else {
+                                       /* invert state bits */
+                                       via[B] ^= ST_MASK;
+                               }
+                       }
+                       break;
+
+               case read_done:
+                       x = via[SR];
+                       if (reading_reply) {
+                               req = current_req;
+                               req->reply_len = reply_ptr - req->reply;
+                               req->complete = 1;
+                               current_req = req->next;
+                               if (req->done) (*req->done)(req);
+                       } else {
+                               adb_input(reply_buf, reply_ptr - reply_buf,
+                                         regs, 0);
+                       }
+
+                       /*
+                        * remember this device ID; it's the latest we got a 
+                        * reply from!
+                        */
+                       last_reply = command_byte;
+                       last_active = (command_byte & 0xF0) >> 4;
+
+                       /* SRQ seen before, initiate poll now */
+                       if (need_poll) {
+                               macii_state = idle;
+                               macii_queue_poll();
+                               need_poll = 0;
+                               break;
+                       }
+
+                       /* /IRQ seen, so the ADB controller has data for us */
+                       if (!(status & TREQ)) {
+                               /* set ADB state to idle */
+                               via[B] = (via[B] & ~ST_MASK) | ST_IDLE;
+
+                               macii_state = reading;
+
+                               reply_buf[0] = command_byte;
+                               reply_ptr = reply_buf + 1;
+                               reply_len = 1;
+                               prefix_len = 1;
+                               reading_reply = 0;
+                       } else {
+                               /* no IRQ, send next packet or wait */
+                               macii_state = idle;
+                               if (current_req)
+                                       macii_start();
+                               else
+                                       macii_retransmit(last_active);
+                       }
+                       break;
+
+               default:
+               break;
+       }
+       /* reset mutex and interrupts */
+       driver_running = 0;
+       restore_flags(flags);
+}
diff --git a/drivers/macintosh/via-maciisi.c b/drivers/macintosh/via-maciisi.c
new file mode 100644 (file)
index 0000000..04ce095
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+ * Device driver for the IIsi-style ADB on some Mac LC and II-class machines
+ *
+ * Based on via-cuda.c and via-macii.c, as well as the original
+ * adb-bus.c, which in turn is somewhat influenced by (but uses no
+ * code from) the NetBSD HWDIRECT ADB code.  Original IIsi driver work
+ * was done by Robert Thompson and integrated into the old style
+ * driver by Michael Schmitz.
+ *
+ * Original sources (c) Alan Cox, Paul Mackerras, and others.
+ *
+ * Rewritten for Unified ADB by David Huggins-Daines <dhd@debian.org> */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/adb.h>
+#include <linux/cuda.h>
+#include <linux/delay.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+#include <asm/mac_via.h>
+
+static volatile unsigned char *via;
+
+/* VIA registers - spaced 0x200 bytes apart - only the ones we actually use */
+#define RS             0x200           /* skip between registers */
+#define B              0               /* B-side data */
+#define A              RS              /* A-side data */
+#define DIRB           (2*RS)          /* B-side direction (1=output) */
+#define DIRA           (3*RS)          /* A-side direction (1=output) */
+#define SR             (10*RS)         /* Shift register */
+#define ACR            (11*RS)         /* Auxiliary control register */
+#define IFR            (13*RS)         /* Interrupt flag register */
+#define IER            (14*RS)         /* Interrupt enable register */
+
+/* Bits in B data register: all active low */
+#define TREQ           0x08            /* Transfer request (input) */
+#define TACK           0x10            /* Transfer acknowledge (output) */
+#define TIP            0x20            /* Transfer in progress (output) */
+#define ST_MASK                0x30            /* mask for selecting ADB state bits */
+
+/* Bits in ACR */
+#define SR_CTRL                0x1c            /* Shift register control bits */
+#define SR_EXT         0x0c            /* Shift on external clock */
+#define SR_OUT         0x10            /* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET                0x80            /* set bits in IER */
+#define IER_CLR                0               /* clear bits in IER */
+#define SR_INT         0x04            /* Shift register full/empty */
+#define SR_DATA                0x08            /* Shift register data */
+#define SR_CLOCK       0x10            /* Shift register clock */
+
+#define ADB_DELAY 150
+
+static struct adb_request* current_req = NULL;
+static struct adb_request* last_req = NULL;
+static unsigned char maciisi_rbuf[16];
+static unsigned char *reply_ptr = NULL;
+static int data_index;
+static int reading_reply;
+static int reply_len;
+
+static enum maciisi_state {
+    idle,
+    sending,
+    reading,
+} maciisi_state;
+
+static int maciisi_probe(void);
+static int maciisi_init(void);
+static int maciisi_send_request(struct adb_request* req, int sync);
+static int maciisi_write(struct adb_request* req);
+static void maciisi_interrupt(int irq, void* arg, struct pt_regs* regs);
+static void maciisi_input(unsigned char *buf, int nb, struct pt_regs *regs);
+static int maciisi_init_via(void);
+static void maciisi_poll(void);
+static void maciisi_start(void);
+
+struct adb_driver via_maciisi_driver = {
+       "Mac IIsi",
+       maciisi_probe,
+       maciisi_init,
+       maciisi_send_request,
+       NULL, /* maciisi_adb_autopoll, */
+       maciisi_poll,
+       NULL /* maciisi_reset_adb_bus */
+};
+
+static int
+maciisi_probe(void)
+{
+       if (macintosh_config->adb_type != MAC_ADB_IISI)
+               return -ENODEV;
+
+       via = via1;
+       return 0;
+}
+
+static int
+maciisi_init(void)
+{
+       int err;
+
+       if (via == NULL)
+               return -ENODEV;
+
+       if ((err = maciisi_init_via())) {
+               printk(KERN_ERR "maciisi_init: maciisi_init_via() failed, code %d\n", err);
+               via = NULL;
+               return err;
+       }
+
+       if (request_irq(IRQ_MAC_ADB, maciisi_interrupt, IRQ_FLG_LOCK, 
+                       "ADB", maciisi_interrupt)) {
+               printk(KERN_ERR "maciisi_init: can't get irq %d\n", IRQ_MAC_ADB);
+               return -EAGAIN;
+       }
+
+       printk("adb: Mac IIsi driver v0.1 for Unified ADB.\n");
+       return 0;
+}
+
+static void
+maciisi_stfu(void)
+{
+       int status = via[B] & (TIP|TREQ);
+
+       if (status & TREQ) {
+               printk (KERN_DEBUG "maciisi_stfu called with TREQ high!\n");
+               return;
+       }
+
+       /* start transfer */
+       via[B] |= TIP;
+       while (!(status & TREQ)) {
+               int poll_timeout = ADB_DELAY * 5;
+               /* Poll for SR interrupt */
+               while (!(via[IFR] & SR_INT) && poll_timeout-- > 0)
+                       status = via[B] & (TIP|TREQ);
+               via[SR]; /* Clear shift register */
+               printk(KERN_DEBUG "maciisi_stfu: status %x timeout %d\n",
+                      status, poll_timeout);
+               
+               /* ACK on-off */
+               via[B] |= TACK;
+               udelay(ADB_DELAY);
+               via[B] &= ~TACK;
+       }
+       /* end frame */
+       via[B] &= ~TIP;
+}
+
+/* All specifically VIA-related initialization goes here */
+static int
+maciisi_init_via(void)
+{
+       /* Set the lines up. We want TREQ as input TACK|TIP as output */
+       via[DIRB] = (via[DIRB] | TACK | TIP) & ~TREQ;
+       /* Shift register on input */
+       via[ACR]  = (via[ACR] & ~SR_CTRL) | SR_EXT;
+       printk(KERN_DEBUG "maciisi_init_via: initial status %x\n", via[B] & (TIP|TREQ));
+       /* Set initial state: idle */
+       via[B] &= ~(TACK|TIP);
+       /* Wipe any pending data and int */
+       via[SR];
+       if (!(via[B] & TREQ))
+               maciisi_stfu();
+       via[IER] = IER_SET | SR_INT;
+       maciisi_state = idle;
+       return 0;
+}
+
+/* Send a request, possibly waiting for a reply */
+static int
+maciisi_send_request(struct adb_request* req, int sync)
+{
+       int i;
+       static int dump_packet = 1;
+
+       if (via == NULL) {
+               req->complete = 1;
+               return -ENXIO;
+       }
+
+       if (dump_packet) {
+               printk(KERN_DEBUG "maciisi_send_request:");
+               for (i = 0; i < req->nbytes; i++) {
+                       printk(" %.2x", req->data[i]);
+               }
+               printk("\n");
+       }
+       req->reply_expected = 1;
+       
+       i = maciisi_write(req);
+       if (i)
+               return i;
+       
+       if (sync) {
+               while (!req->complete) {
+                       maciisi_poll();
+               }
+       }
+       return 0;
+}
+
+/* Enqueue a request, and run the queue if possible */
+static int
+maciisi_write(struct adb_request* req)
+{
+       unsigned long flags;
+
+       printk(KERN_DEBUG "maciisi_write called, state=%d ifr=%x\n", maciisi_state, via[IFR]);
+       /* We will accept CUDA packets - the VIA sends them to us, so
+           it figures that we should be able to send them to it */
+       if (req->nbytes < 2 || req->data[0] > CUDA_PACKET) {
+               printk(KERN_ERR "maciisi_write: packet too small or not an ADB or CUDA packet\n");
+               req->complete = 1;
+               return -EINVAL;
+       }
+       req->next = 0;
+       req->sent = 0;
+       req->complete = 0;
+       req->reply_len = 0;
+       save_flags(flags); cli();
+
+       if (current_req) {
+               last_req->next = req;
+               last_req = req;
+       } else {
+               current_req = req;
+               last_req = req;
+       }
+       if (maciisi_state == idle)
+               maciisi_start();
+       else
+               printk(KERN_DEBUG "maciisi_write: would start, but state is %d\n", maciisi_state);
+
+       restore_flags(flags);
+       return 0;
+}
+
+static void
+maciisi_start(void)
+{
+       struct adb_request* req;
+       int status;
+
+       printk(KERN_DEBUG "maciisi_start called, state=%d, ifr=%x\n", maciisi_state, via[IFR]);
+       if (maciisi_state != idle) {
+               /* shouldn't happen */
+               printk(KERN_ERR "maciisi_start: maciisi_start called when driver busy!\n");
+               return;
+       }
+
+       req = current_req;
+       if (req == NULL)
+               return;
+
+       status = via[B] & (TIP|TREQ);
+       if (!(status & TREQ)) {
+               /* Bus is busy, set up for reading */
+               printk(KERN_DEBUG "maciisi_start: bus busy - aborting\n");
+               return;
+       }
+
+       /* Okay, send */
+       printk(KERN_DEBUG "maciisi_start: sending\n");
+       /* Set state to active */
+       via[B] |= TIP;
+       /* ACK off */
+       via[B] &= ~TACK;
+       /* Shift out and send */
+       via[ACR] |= SR_OUT;
+       via[SR] = req->data[0];
+       data_index = 1;
+       /* ACK on */
+       via[B] |= TACK;
+       maciisi_state = sending;
+}
+
+void
+maciisi_poll(void)
+{
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       if (via[IFR] & SR_INT) {
+               maciisi_interrupt(0, 0, 0);
+       }
+       restore_flags(flags);
+}
+
+/* Shift register interrupt - this is *supposed* to mean that the
+   register is either full or empty. In practice, I have no idea what
+   it means :( */
+static void
+maciisi_interrupt(int irq, void* arg, struct pt_regs* regs)
+{
+       int status;
+       struct adb_request *req;
+       static int dump_reply = 1;
+
+       if (!(via[IFR] & SR_INT)) {
+               /* Shouldn't happen, we hope */
+               printk(KERN_DEBUG "maciisi_interrupt: called without interrupt flag set\n");
+               return;
+       }
+
+       status = via[B] & (TIP|TREQ);
+       printk(KERN_DEBUG "state %d status %x ifr %x\n", maciisi_state, status, via[IFR]);
+
+ switch_start:
+       switch (maciisi_state) {
+       case idle:
+               printk(KERN_DEBUG "maciisi_interrupt: state=idle, status %x\n", status);
+               if (status & TIP)
+                       printk(KERN_DEBUG "maciisi_interrupt: state is idle but TIP asserted!\n");
+
+               udelay(ADB_DELAY);
+               /* Shift in */
+               via[ACR] &= ~SR_OUT;
+               /* Signal start of frame */
+               via[B] |= TIP;
+               /* Clear the interrupt (throw this value on the floor, it's useless) */
+               via[SR];
+               /* ACK adb chip, high-low */
+               via[B] |= TACK;
+               udelay(ADB_DELAY);
+               via[B] &= ~TACK;
+               reply_len = 0;
+               maciisi_state = reading;
+               if (reading_reply) {
+                       reply_ptr = current_req->reply;
+               } else {
+                       printk(KERN_DEBUG "maciisi_interrupt: received unsolicited packet\n");
+                       reply_ptr = maciisi_rbuf;
+               }
+               break;
+
+       case sending:
+               printk(KERN_DEBUG "maciisi_interrupt: state=sending, status=%x\n", status);
+               /* Clear interrupt */
+               via[SR];
+               /* Set ACK off */
+               via[B] &= ~TACK;
+               req = current_req;
+
+               if (!(status & TREQ)) {
+                       /* collision */
+                       printk(KERN_DEBUG "maciisi_interrupt: send collision\n");
+                       /* Set idle and input */
+                       via[B] &= ~TIP;
+                       via[ACR] &= ~SR_OUT;
+                       /* Must re-send */
+                       reading_reply = 0;
+                       reply_len = 0;
+                       maciisi_state = idle;
+                       /* process this now, because the IFR has been cleared */
+                       goto switch_start;
+               }
+
+               if (data_index >= req->nbytes) {
+                       /* Sent the whole packet, put the bus back in idle state */
+                       /* Shift in, we are about to read a reply (hopefully) */
+                       via[ACR] &= ~SR_OUT;
+                       /* End of frame */
+                       via[B] &= ~TIP;
+                       req->sent = 1;
+                       maciisi_state = idle;
+                       if (req->reply_expected) {
+                               /* Note: only set this once we've
+                                   successfully sent the packet */
+                               reading_reply = 1;
+                       } else {
+                               current_req = req->next;
+                               if (req->done)
+                                       (*req->done)(req);
+                       }
+               } else {
+                       /* Sending more stuff */
+                       /* Shift out */
+                       via[ACR] |= SR_OUT;
+                       /* Delay */
+                       udelay(ADB_DELAY);
+                       /* Write */
+                       via[SR] = req->data[data_index++];
+                       /* Signal 'byte ready' */
+                       via[B] |= TACK;
+               }
+               break;
+
+       case reading:
+               printk(KERN_DEBUG "maciisi_interrupt: state=reading, status=%x\n", status);
+               /* Shift in */
+               via[ACR] &= ~SR_OUT;
+               if (reply_len++ > 16) {
+                       printk(KERN_ERR "maciisi_interrupt: reply too long, aborting read\n");
+                       via[B] |= TACK;
+                       udelay(ADB_DELAY);
+                       via[B] &= ~(TACK|TIP);
+                       maciisi_state = idle;
+                       maciisi_start();
+                       break;
+               }
+               *reply_ptr++ = via[SR];
+               status = via[B] & (TIP|TREQ);
+               /* ACK on/off */
+               via[B] |= TACK;
+               udelay(ADB_DELAY);
+               via[B] &= ~TACK;        
+               if (!(status & TREQ))
+                       break; /* more stuff to deal with */
+               
+               /* end of frame */
+               via[B] &= ~TIP;
+
+               /* end of packet, deal with it */
+               if (reading_reply) {
+                       req = current_req;
+                       req->reply_len = reply_ptr - req->reply;
+                       if (req->data[0] == ADB_PACKET) {
+                               /* Have to adjust the reply from ADB commands */
+                               if (req->reply_len <= 2 || (req->reply[1] & 2) != 0) {
+                                       /* the 0x2 bit indicates no response */
+                                       req->reply_len = 0;
+                               } else {
+                                       /* leave just the command and result bytes in the reply */
+                                       req->reply_len -= 2;
+                                       memmove(req->reply, req->reply + 2, req->reply_len);
+                               }
+                       }
+                       if (dump_reply) {
+                               int i;
+                               printk(KERN_DEBUG "maciisi_interrupt: reply is ");
+                               for (i = 0; i < req->reply_len; ++i)
+                                       printk(" %.2x", req->reply[i]);
+                               printk("\n");
+                       }
+                       req->complete = 1;
+                       current_req = req->next;
+                       if (req->done)
+                               (*req->done)(req);
+                       /* Obviously, we got it */
+                       reading_reply = 0;
+               } else {
+                       maciisi_input(maciisi_rbuf, reply_ptr - maciisi_rbuf, regs);
+               }
+               maciisi_state = idle;
+               status = via[B] & (TIP|TREQ);
+               if (!(status & TREQ)) {
+                       /* Timeout?! */
+                       printk(KERN_DEBUG "extra data after packet: status %x ifr %x\n",
+                              status, via[IFR]);
+                       maciisi_stfu();
+               }
+               /* Do any queued requests now if possible */
+               maciisi_start();
+               break;
+
+       default:
+               printk("maciisi_interrupt: unknown maciisi_state %d?\n", maciisi_state);
+       }
+}
+
+static void
+maciisi_input(unsigned char *buf, int nb, struct pt_regs *regs)
+{
+    int i;
+
+    switch (buf[0]) {
+    case ADB_PACKET:
+           adb_input(buf+2, nb-2, regs, buf[1] & 0x40);
+           break;
+    default:
+           printk(KERN_DEBUG "data from IIsi ADB (%d bytes):", nb);
+           for (i = 0; i < nb; ++i)
+                   printk(" %.2x", buf[i]);
+           printk("\n");
+
+    }
+}
diff --git a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c
new file mode 100644 (file)
index 0000000..bdb9fae
--- /dev/null
@@ -0,0 +1,1064 @@
+/*
+ * Device driver for the PMU on 68K-based Apple PowerBooks
+ *
+ * The VIA (versatile interface adapter) interfaces to the PMU,
+ * a 6805 microprocessor core whose primary function is to control
+ * battery charging and system power on the PowerBooks.
+ * The PMU also controls the ADB (Apple Desktop Bus) which connects
+ * to the keyboard and mouse, as well as the non-volatile RAM
+ * and the RTC (real time clock) chip.
+ *
+ * Adapted for 68K PMU by Joshua M. Thompson
+ *
+ * Based largely on the PowerMac PMU code by Paul Mackerras and
+ * Fabio Riccardi.
+ *
+ * Also based on the PMU driver from MkLinux by Apple Computer, Inc.
+ * and the Open Software Foundation, Inc.
+ */
+
+#include <stdarg.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/blkdev.h>
+#include <linux/pci.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <linux/cuda.h>
+
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+#include <asm/machw.h>
+#include <asm/mac_via.h>
+
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* Misc minor number allocated for /dev/pmu */
+#define PMU_MINOR      154
+
+/* VIA registers - spaced 0x200 bytes apart */
+#define RS             0x200           /* skip between registers */
+#define B              0               /* B-side data */
+#define A              RS              /* A-side data */
+#define DIRB           (2*RS)          /* B-side direction (1=output) */
+#define DIRA           (3*RS)          /* A-side direction (1=output) */
+#define T1CL           (4*RS)          /* Timer 1 ctr/latch (low 8 bits) */
+#define T1CH           (5*RS)          /* Timer 1 counter (high 8 bits) */
+#define T1LL           (6*RS)          /* Timer 1 latch (low 8 bits) */
+#define T1LH           (7*RS)          /* Timer 1 latch (high 8 bits) */
+#define T2CL           (8*RS)          /* Timer 2 ctr/latch (low 8 bits) */
+#define T2CH           (9*RS)          /* Timer 2 counter (high 8 bits) */
+#define SR             (10*RS)         /* Shift register */
+#define ACR            (11*RS)         /* Auxiliary control register */
+#define PCR            (12*RS)         /* Peripheral control register */
+#define IFR            (13*RS)         /* Interrupt flag register */
+#define IER            (14*RS)         /* Interrupt enable register */
+#define ANH            (15*RS)         /* A-side data, no handshake */
+
+/* Bits in B data register: both active low */
+#define TACK           0x02            /* Transfer acknowledge (input) */
+#define TREQ           0x04            /* Transfer request (output) */
+
+/* Bits in ACR */
+#define SR_CTRL                0x1c            /* Shift register control bits */
+#define SR_EXT         0x0c            /* Shift on external clock */
+#define SR_OUT         0x10            /* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define SR_INT         0x04            /* Shift register full/empty */
+#define CB1_INT                0x10            /* transition on CB1 input */
+
+static enum pmu_state {
+       idle,
+       sending,
+       intack,
+       reading,
+       reading_intr,
+} pmu_state;
+
+static struct adb_request *current_req;
+static struct adb_request *last_req;
+static struct adb_request *req_awaiting_reply;
+static unsigned char interrupt_data[32];
+static unsigned char *reply_ptr;
+static int data_index;
+static int data_len;
+static int adb_int_pending;
+static int pmu_adb_flags;
+static int adb_dev_map = 0;
+static struct adb_request bright_req_1, bright_req_2, bright_req_3;
+static int pmu_kind = PMU_UNKNOWN;
+static int pmu_fully_inited = 0;
+
+int asleep;
+struct notifier_block *sleep_notifier_list;
+
+static int pmu_probe(void);
+static int pmu_init(void);
+static void pmu_start(void);
+static void pmu_interrupt(int irq, void *arg, struct pt_regs *regs);
+static int pmu_send_request(struct adb_request *req, int sync);
+static int pmu_autopoll(int devs);
+void pmu_poll(void);
+static int pmu_reset_bus(void);
+static int pmu_queue_request(struct adb_request *req);
+
+static void pmu_start(void);
+static void send_byte(int x);
+static void recv_byte(void);
+static void pmu_done(struct adb_request *req);
+static void pmu_handle_data(unsigned char *data, int len,
+                           struct pt_regs *regs);
+static void set_volume(int level);
+
+struct adb_driver via_pmu_driver = {
+       "68K PMU",
+       pmu_probe,
+       pmu_init,
+       pmu_send_request,
+       pmu_autopoll,
+       pmu_poll,
+       pmu_reset_bus
+};
+
+/*
+ * This table indicates for each PMU opcode:
+ * - the number of data bytes to be sent with the command, or -1
+ *   if a length byte should be sent,
+ * - the number of response bytes which the PMU will return, or
+ *   -1 if it will send a length byte.
+ */
+static s8 pmu_data_len[256][2] = {
+/*        0       1       2       3       4       5       6       7  */
+/*00*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*08*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*10*/ { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*18*/ { 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0, 0},
+/*20*/ {-1, 0},{ 0, 0},{ 2, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*28*/ { 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{ 0,-1},
+/*30*/ { 4, 0},{20, 0},{-1, 0},{ 3, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*38*/ { 0, 4},{ 0,20},{ 2,-1},{ 2, 1},{ 3,-1},{-1,-1},{-1,-1},{ 4, 0},
+/*40*/ { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*48*/ { 0, 1},{ 0, 1},{-1,-1},{ 1, 0},{ 1, 0},{-1,-1},{-1,-1},{-1,-1},
+/*50*/ { 1, 0},{ 0, 0},{ 2, 0},{ 2, 0},{-1, 0},{ 1, 0},{ 3, 0},{ 1, 0},
+/*58*/ { 0, 1},{ 1, 0},{ 0, 2},{ 0, 2},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},
+/*60*/ { 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*68*/ { 0, 3},{ 0, 3},{ 0, 2},{ 0, 8},{ 0,-1},{ 0,-1},{-1,-1},{-1,-1},
+/*70*/ { 1, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*78*/ { 0,-1},{ 0,-1},{-1,-1},{-1,-1},{-1,-1},{ 5, 1},{ 4, 1},{ 4, 1},
+/*80*/ { 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*88*/ { 0, 5},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*90*/ { 1, 0},{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*98*/ { 0, 1},{ 0, 1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*a0*/ { 2, 0},{ 2, 0},{ 2, 0},{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},
+/*a8*/ { 1, 1},{ 1, 0},{ 3, 0},{ 2, 0},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*b0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*b8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*c0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*c8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+/*d0*/ { 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*d8*/ { 1, 1},{ 1, 1},{-1,-1},{-1,-1},{ 0, 1},{ 0,-1},{-1,-1},{-1,-1},
+/*e0*/ {-1, 0},{ 4, 0},{ 0, 1},{-1, 0},{-1, 0},{ 4, 0},{-1, 0},{-1, 0},
+/*e8*/ { 3,-1},{-1,-1},{ 0, 1},{-1,-1},{ 0,-1},{-1,-1},{-1,-1},{ 0, 0},
+/*f0*/ {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},
+/*f8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
+};
+
+int pmu_probe()
+{
+       if (macintosh_config->adb_type == MAC_ADB_PB1) {
+               pmu_kind = PMU_68K_V1;
+       } else if (macintosh_config->adb_type == MAC_ADB_PB2) {
+               pmu_kind = PMU_68K_V2;
+       } else {
+               return -ENODEV;
+       }
+
+       pmu_state = idle;
+
+       return 0;
+}
+
+static int 
+pmu_init(void)
+{
+       int timeout;
+       volatile struct adb_request req;
+
+       via2[B] |= TREQ;                                /* negate TREQ */
+       via2[DIRB] = (via2[DIRB] | TREQ) & ~TACK;       /* TACK in, TREQ out */
+
+       pmu_request((struct adb_request *) &req, NULL, 2, PMU_SET_INTR_MASK, PMU_INT_ADB);
+       timeout =  100000;
+       while (!req.complete) {
+               if (--timeout < 0) {
+                       printk(KERN_ERR "pmu_init: no response from PMU\n");
+                       return -EAGAIN;
+               }
+               udelay(10);
+               pmu_poll();
+       }
+
+       /* ack all pending interrupts */
+       timeout = 100000;
+       interrupt_data[0] = 1;
+       while (interrupt_data[0] || pmu_state != idle) {
+               if (--timeout < 0) {
+                       printk(KERN_ERR "pmu_init: timed out acking intrs\n");
+                       return -EAGAIN;
+               }
+               if (pmu_state == idle) {
+                       adb_int_pending = 1;
+                       pmu_interrupt(0, NULL, NULL);
+               }
+               pmu_poll();
+               udelay(10);
+       }
+
+       pmu_request((struct adb_request *) &req, NULL, 2, PMU_SET_INTR_MASK,
+                       PMU_INT_ADB_AUTO|PMU_INT_SNDBRT|PMU_INT_ADB);
+       timeout =  100000;
+       while (!req.complete) {
+               if (--timeout < 0) {
+                       printk(KERN_ERR "pmu_init: no response from PMU\n");
+                       return -EAGAIN;
+               }
+               udelay(10);
+               pmu_poll();
+       }
+
+       bright_req_1.complete = 1;
+       bright_req_2.complete = 1;
+       bright_req_3.complete = 1;
+
+       if (request_irq(IRQ_MAC_ADB_SR, pmu_interrupt, 0, "pmu-shift",
+                       pmu_interrupt)) {
+               printk(KERN_ERR "pmu_init: can't get irq %d\n",
+                       IRQ_MAC_ADB_SR);
+               return -EAGAIN;
+       }
+       if (request_irq(IRQ_MAC_ADB_CL, pmu_interrupt, 0, "pmu-clock",
+                       pmu_interrupt)) {
+               printk(KERN_ERR "pmu_init: can't get irq %d\n",
+                       IRQ_MAC_ADB_CL);
+               free_irq(IRQ_MAC_ADB_SR, pmu_interrupt);
+               return -EAGAIN;
+       }
+
+       pmu_fully_inited = 1;
+       
+       /* Enable backlight */
+       pmu_enable_backlight(1);
+
+       printk("adb: PMU 68K driver v0.5 for Unified ADB.\n");
+
+       return 0;
+}
+
+int
+pmu_get_model(void)
+{
+       return pmu_kind;
+}
+
+/* Send an ADB command */
+static int 
+pmu_send_request(struct adb_request *req, int sync)
+{
+    int i, ret;
+
+    if (!pmu_fully_inited)
+    {
+       req->complete = 1;
+       return -ENXIO;
+   }
+
+    ret = -EINVAL;
+       
+    switch (req->data[0]) {
+    case PMU_PACKET:
+               for (i = 0; i < req->nbytes - 1; ++i)
+                       req->data[i] = req->data[i+1];
+               --req->nbytes;
+               if (pmu_data_len[req->data[0]][1] != 0) {
+                       req->reply[0] = ADB_RET_OK;
+                       req->reply_len = 1;
+               } else
+                       req->reply_len = 0;
+               ret = pmu_queue_request(req);
+               break;
+    case CUDA_PACKET:
+               switch (req->data[1]) {
+               case CUDA_GET_TIME:
+                       if (req->nbytes != 2)
+                               break;
+                       req->data[0] = PMU_READ_RTC;
+                       req->nbytes = 1;
+                       req->reply_len = 3;
+                       req->reply[0] = CUDA_PACKET;
+                       req->reply[1] = 0;
+                       req->reply[2] = CUDA_GET_TIME;
+                       ret = pmu_queue_request(req);
+                       break;
+               case CUDA_SET_TIME:
+                       if (req->nbytes != 6)
+                               break;
+                       req->data[0] = PMU_SET_RTC;
+                       req->nbytes = 5;
+                       for (i = 1; i <= 4; ++i)
+                               req->data[i] = req->data[i+1];
+                       req->reply_len = 3;
+                       req->reply[0] = CUDA_PACKET;
+                       req->reply[1] = 0;
+                       req->reply[2] = CUDA_SET_TIME;
+                       ret = pmu_queue_request(req);
+                       break;
+               case CUDA_GET_PRAM:
+                       if (req->nbytes != 4)
+                               break;
+                       req->data[0] = PMU_READ_NVRAM;
+                       req->data[1] = req->data[2];
+                       req->data[2] = req->data[3];
+                       req->nbytes = 3;
+                       req->reply_len = 3;
+                       req->reply[0] = CUDA_PACKET;
+                       req->reply[1] = 0;
+                       req->reply[2] = CUDA_GET_PRAM;
+                       ret = pmu_queue_request(req);
+                       break;
+               case CUDA_SET_PRAM:
+                       if (req->nbytes != 5)
+                               break;
+                       req->data[0] = PMU_WRITE_NVRAM;
+                       req->data[1] = req->data[2];
+                       req->data[2] = req->data[3];
+                       req->data[3] = req->data[4];
+                       req->nbytes = 4;
+                       req->reply_len = 3;
+                       req->reply[0] = CUDA_PACKET;
+                       req->reply[1] = 0;
+                       req->reply[2] = CUDA_SET_PRAM;
+                       ret = pmu_queue_request(req);
+                       break;
+               }
+               break;
+    case ADB_PACKET:
+               for (i = req->nbytes - 1; i > 1; --i)
+                       req->data[i+2] = req->data[i];
+               req->data[3] = req->nbytes - 2;
+               req->data[2] = pmu_adb_flags;
+               /*req->data[1] = req->data[1];*/
+               req->data[0] = PMU_ADB_CMD;
+               req->nbytes += 2;
+               req->reply_expected = 1;
+               req->reply_len = 0;
+               ret = pmu_queue_request(req);
+               break;
+    }
+    if (ret)
+    {
+       req->complete = 1;
+       return ret;
+    }
+       
+    if (sync) {
+       while (!req->complete)
+               pmu_poll();
+    }
+
+    return 0;
+}
+
+/* Enable/disable autopolling */
+static int 
+pmu_autopoll(int devs)
+{
+       struct adb_request req;
+
+       if (!pmu_fully_inited) return -ENXIO;
+
+       if (devs) {
+               adb_dev_map = devs;
+               pmu_request(&req, NULL, 5, PMU_ADB_CMD, 0, 0x86,
+                           adb_dev_map >> 8, adb_dev_map);
+               pmu_adb_flags = 2;
+       } else {
+               pmu_request(&req, NULL, 1, PMU_ADB_POLL_OFF);
+               pmu_adb_flags = 0;
+       }
+       while (!req.complete)
+               pmu_poll();
+       return 0;
+}
+
+/* Reset the ADB bus */
+static int 
+pmu_reset_bus(void)
+{
+       struct adb_request req;
+       long timeout;
+       int save_autopoll = adb_dev_map;
+
+       if (!pmu_fully_inited) return -ENXIO;
+
+       /* anyone got a better idea?? */
+       pmu_autopoll(0);
+
+       req.nbytes = 5;
+       req.done = NULL;
+       req.data[0] = PMU_ADB_CMD;
+       req.data[1] = 0;
+       req.data[2] = 3; /* ADB_BUSRESET ??? */
+       req.data[3] = 0;
+       req.data[4] = 0;
+       req.reply_len = 0;
+       req.reply_expected = 1;
+       if (pmu_queue_request(&req) != 0)
+       {
+               printk(KERN_ERR "pmu_adb_reset_bus: pmu_queue_request failed\n");
+               return -EIO;
+       }
+       while (!req.complete)
+               pmu_poll();
+       timeout = 100000;
+       while (!req.complete) {
+               if (--timeout < 0) {
+                       printk(KERN_ERR "pmu_adb_reset_bus (reset): no response from PMU\n");
+                       return -EIO;
+               }
+               udelay(10);
+               pmu_poll();
+       }
+
+       if (save_autopoll != 0)
+               pmu_autopoll(save_autopoll);
+               
+       return 0;
+}
+
+/* Construct and send a pmu request */
+int 
+pmu_request(struct adb_request *req, void (*done)(struct adb_request *),
+           int nbytes, ...)
+{
+       va_list list;
+       int i;
+
+       if (nbytes < 0 || nbytes > 32) {
+               printk(KERN_ERR "pmu_request: bad nbytes (%d)\n", nbytes);
+               req->complete = 1;
+               return -EINVAL;
+       }
+       req->nbytes = nbytes;
+       req->done = done;
+       va_start(list, nbytes);
+       for (i = 0; i < nbytes; ++i)
+               req->data[i] = va_arg(list, int);
+       va_end(list);
+       if (pmu_data_len[req->data[0]][1] != 0) {
+               req->reply[0] = ADB_RET_OK;
+               req->reply_len = 1;
+       } else
+               req->reply_len = 0;
+       req->reply_expected = 0;
+       return pmu_queue_request(req);
+}
+
+static int 
+pmu_queue_request(struct adb_request *req)
+{
+       unsigned long flags;
+       int nsend;
+
+       if (req->nbytes <= 0) {
+               req->complete = 1;
+               return 0;
+       }
+       nsend = pmu_data_len[req->data[0]][0];
+       if (nsend >= 0 && req->nbytes != nsend + 1) {
+               req->complete = 1;
+               return -EINVAL;
+       }
+
+       req->next = 0;
+       req->sent = 0;
+       req->complete = 0;
+       save_flags(flags); cli();
+
+       if (current_req != 0) {
+               last_req->next = req;
+               last_req = req;
+       } else {
+               current_req = req;
+               last_req = req;
+               if (pmu_state == idle)
+                       pmu_start();
+       }
+
+       restore_flags(flags);
+       return 0;
+}
+
+static void 
+send_byte(int x)
+{
+       via1[ACR] |= SR_CTRL;
+       via1[SR] = x;
+       via2[B] &= ~TREQ;               /* assert TREQ */
+}
+
+static void 
+recv_byte()
+{
+       char c;
+
+       via1[ACR] = (via1[ACR] | SR_EXT) & ~SR_OUT;
+       c = via1[SR];           /* resets SR */
+       via2[B] &= ~TREQ;
+}
+
+static void 
+pmu_start()
+{
+       unsigned long flags;
+       struct adb_request *req;
+
+       /* assert pmu_state == idle */
+       /* get the packet to send */
+       save_flags(flags); cli();
+       req = current_req;
+       if (req == 0 || pmu_state != idle
+           || (req->reply_expected && req_awaiting_reply))
+               goto out;
+
+       pmu_state = sending;
+       data_index = 1;
+       data_len = pmu_data_len[req->data[0]][0];
+
+       /* set the shift register to shift out and send a byte */
+       send_byte(req->data[0]);
+
+out:
+       restore_flags(flags);
+}
+
+void 
+pmu_poll()
+{
+       unsigned long cpu_flags;
+
+       save_flags(cpu_flags);
+       cli();
+       if (via1[IFR] & SR_INT) {
+               via1[IFR] = SR_INT;
+               pmu_interrupt(IRQ_MAC_ADB_SR, NULL, NULL);
+       }
+       if (via1[IFR] & CB1_INT) {
+               via1[IFR] = CB1_INT;
+               pmu_interrupt(IRQ_MAC_ADB_CL, NULL, NULL);
+       }
+       restore_flags(cpu_flags);
+}
+
+static void 
+pmu_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct adb_request *req;
+       int timeout, bite = 0;  /* to prevent compiler warning */
+
+#if 0
+       printk("pmu_interrupt: irq %d state %d acr %02X, b %02X data_index %d/%d adb_int_pending %d\n",
+               irq, pmu_state, (uint) via1[ACR], (uint) via2[B], data_index, data_len, adb_int_pending);
+#endif
+
+       if (irq == IRQ_MAC_ADB_CL) {            /* CB1 interrupt */
+               adb_int_pending = 1;
+       } else if (irq == IRQ_MAC_ADB_SR) {     /* SR interrupt  */
+               if (via2[B] & TACK) {
+                       printk(KERN_DEBUG "PMU: SR_INT but ack still high! (%x)\n", via2[B]);
+               }
+
+               /* if reading grab the byte */
+               if ((via1[ACR] & SR_OUT) == 0) bite = via1[SR];
+
+               /* reset TREQ and wait for TACK to go high */
+               via2[B] |= TREQ;
+               timeout = 3200;
+               while (!(via2[B] & TACK)) {
+                       if (--timeout < 0) {
+                               printk(KERN_ERR "PMU not responding (!ack)\n");
+                               goto finish;
+                       }
+                       udelay(10);
+               }
+
+               switch (pmu_state) {
+               case sending:
+                       req = current_req;
+                       if (data_len < 0) {
+                               data_len = req->nbytes - 1;
+                               send_byte(data_len);
+                               break;
+                       }
+                       if (data_index <= data_len) {
+                               send_byte(req->data[data_index++]);
+                               break;
+                       }
+                       req->sent = 1;
+                       data_len = pmu_data_len[req->data[0]][1];
+                       if (data_len == 0) {
+                               pmu_state = idle;
+                               current_req = req->next;
+                               if (req->reply_expected)
+                                       req_awaiting_reply = req;
+                               else
+                                       pmu_done(req);
+                       } else {
+                               pmu_state = reading;
+                               data_index = 0;
+                               reply_ptr = req->reply + req->reply_len;
+                               recv_byte();
+                       }
+                       break;
+
+               case intack:
+                       data_index = 0;
+                       data_len = -1;
+                       pmu_state = reading_intr;
+                       reply_ptr = interrupt_data;
+                       recv_byte();
+                       break;
+
+               case reading:
+               case reading_intr:
+                       if (data_len == -1) {
+                               data_len = bite;
+                               if (bite > 32)
+                                       printk(KERN_ERR "PMU: bad reply len %d\n",
+                                              bite);
+                       } else {
+                               reply_ptr[data_index++] = bite;
+                       }
+                       if (data_index < data_len) {
+                               recv_byte();
+                               break;
+                       }
+
+                       if (pmu_state == reading_intr) {
+                               pmu_handle_data(interrupt_data, data_index, regs);
+                       } else {
+                               req = current_req;
+                               current_req = req->next;
+                               req->reply_len += data_index;
+                               pmu_done(req);
+                       }
+                       pmu_state = idle;
+
+                       break;
+
+               default:
+                       printk(KERN_ERR "pmu_interrupt: unknown state %d?\n",
+                              pmu_state);
+               }
+       }
+finish:
+       if (pmu_state == idle) {
+               if (adb_int_pending) {
+                       pmu_state = intack;
+                       send_byte(PMU_INT_ACK);
+                       adb_int_pending = 0;
+               } else if (current_req) {
+                       pmu_start();
+               }
+       }
+
+#if 0
+       printk("pmu_interrupt: exit state %d acr %02X, b %02X data_index %d/%d adb_int_pending %d\n",
+               pmu_state, (uint) via1[ACR], (uint) via2[B], data_index, data_len, adb_int_pending);
+#endif
+}
+
+static void 
+pmu_done(struct adb_request *req)
+{
+       req->complete = 1;
+       if (req->done)
+               (*req->done)(req);
+}
+
+/* Interrupt data could be the result data from an ADB cmd */
+static void 
+pmu_handle_data(unsigned char *data, int len, struct pt_regs *regs)
+{
+       static int show_pmu_ints = 1;
+
+       asleep = 0;
+       if (len < 1) {
+               adb_int_pending = 0;
+               return;
+       }
+       if (data[0] & PMU_INT_ADB) {
+               if ((data[0] & PMU_INT_ADB_AUTO) == 0) {
+                       struct adb_request *req = req_awaiting_reply;
+                       if (req == 0) {
+                               printk(KERN_ERR "PMU: extra ADB reply\n");
+                               return;
+                       }
+                       req_awaiting_reply = 0;
+                       if (len <= 2)
+                               req->reply_len = 0;
+                       else {
+                               memcpy(req->reply, data + 1, len - 1);
+                               req->reply_len = len - 1;
+                       }
+                       pmu_done(req);
+               } else {
+                       adb_input(data+1, len-1, regs, 1);
+               }
+       } else {
+               if (data[0] == 0x08 && len == 3) {
+                       /* sound/brightness buttons pressed */
+                       pmu_set_brightness(data[1] >> 3);
+                       set_volume(data[2]);
+               } else if (show_pmu_ints
+                          && !(data[0] == PMU_INT_TICK && len == 1)) {
+                       int i;
+                       printk(KERN_DEBUG "pmu intr");
+                       for (i = 0; i < len; ++i)
+                               printk(" %.2x", data[i]);
+                       printk("\n");
+               }
+       }
+}
+
+int backlight_level = -1;
+int backlight_enabled = 0;
+
+#define LEVEL_TO_BRIGHT(lev)   ((lev) < 1? 0x7f: 0x4a - ((lev) << 1))
+
+void 
+pmu_enable_backlight(int on)
+{
+       struct adb_request req;
+
+       if (on) {
+           /* first call: get current backlight value */
+           if (backlight_level < 0) {
+               switch(pmu_kind) {
+                   case PMU_68K_V1:
+                   case PMU_68K_V2:
+                       pmu_request(&req, NULL, 3, PMU_READ_NVRAM, 0x14, 0xe);
+                       while (!req.complete)
+                               pmu_poll();
+                       printk(KERN_DEBUG "pmu: nvram returned bright: %d\n", (int)req.reply[1]);
+                       backlight_level = req.reply[1];
+                       break;
+                   default:
+                       backlight_enabled = 0;
+                       return;
+               }
+           }
+           pmu_request(&req, NULL, 2, PMU_BACKLIGHT_BRIGHT,
+               LEVEL_TO_BRIGHT(backlight_level));
+           while (!req.complete)
+               pmu_poll();
+       }
+       pmu_request(&req, NULL, 2, PMU_POWER_CTRL,
+           PMU_POW_BACKLIGHT | (on ? PMU_POW_ON : PMU_POW_OFF));
+       while (!req.complete)
+               pmu_poll();
+       backlight_enabled = on;
+}
+
+void 
+pmu_set_brightness(int level)
+{
+       int bright;
+
+       backlight_level = level;
+       bright = LEVEL_TO_BRIGHT(level);
+       if (!backlight_enabled)
+               return;
+       if (bright_req_1.complete)
+               pmu_request(&bright_req_1, NULL, 2, PMU_BACKLIGHT_BRIGHT,
+                   bright);
+       if (bright_req_2.complete)
+               pmu_request(&bright_req_2, NULL, 2, PMU_POWER_CTRL,
+                   PMU_POW_BACKLIGHT | (bright < 0x7f ? PMU_POW_ON : PMU_POW_OFF));
+}
+
+void 
+pmu_enable_irled(int on)
+{
+       struct adb_request req;
+
+       pmu_request(&req, NULL, 2, PMU_POWER_CTRL, PMU_POW_IRLED |
+           (on ? PMU_POW_ON : PMU_POW_OFF));
+       while (!req.complete)
+               pmu_poll();
+}
+
+static void 
+set_volume(int level)
+{
+}
+
+int
+pmu_present(void)
+{
+       return (pmu_kind != PMU_UNKNOWN);
+}
+
+#if 0 /* needs some work for 68K */
+
+/*
+ * This struct is used to store config register values for
+ * PCI devices which may get powered off when we sleep.
+ */
+static struct pci_save {
+       u16     command;
+       u16     cache_lat;
+       u16     intr;
+} *pbook_pci_saves;
+static int n_pbook_pci_saves;
+
+static inline void __openfirmware
+pbook_pci_save(void)
+{
+       int npci;
+       struct pci_dev *pd;
+       struct pci_save *ps;
+
+       npci = 0;
+       for (pd = pci_devices; pd != NULL; pd = pd->next)
+               ++npci;
+       n_pbook_pci_saves = npci;
+       if (npci == 0)
+               return;
+       ps = (struct pci_save *) kmalloc(npci * sizeof(*ps), GFP_KERNEL);
+       pbook_pci_saves = ps;
+       if (ps == NULL)
+               return;
+
+       for (pd = pci_devices; pd != NULL && npci != 0; pd = pd->next) {
+               pci_read_config_word(pd, PCI_COMMAND, &ps->command);
+               pci_read_config_word(pd, PCI_CACHE_LINE_SIZE, &ps->cache_lat);
+               pci_read_config_word(pd, PCI_INTERRUPT_LINE, &ps->intr);
+               ++ps;
+               --npci;
+       }
+}
+
+static inline void __openfirmware
+pbook_pci_restore(void)
+{
+       u16 cmd;
+       struct pci_save *ps = pbook_pci_saves;
+       struct pci_dev *pd;
+       int j;
+
+       for (pd = pci_devices; pd != NULL; pd = pd->next, ++ps) {
+               if (ps->command == 0)
+                       continue;
+               pci_read_config_word(pd, PCI_COMMAND, &cmd);
+               if ((ps->command & ~cmd) == 0)
+                       continue;
+               switch (pd->hdr_type) {
+               case PCI_HEADER_TYPE_NORMAL:
+                       for (j = 0; j < 6; ++j)
+                               pci_write_config_dword(pd,
+                                       PCI_BASE_ADDRESS_0 + j*4,
+                                       pd->resource[j].start);
+                       pci_write_config_dword(pd, PCI_ROM_ADDRESS,
+                              pd->resource[PCI_ROM_RESOURCE].start);
+                       pci_write_config_word(pd, PCI_CACHE_LINE_SIZE,
+                               ps->cache_lat);
+                       pci_write_config_word(pd, PCI_INTERRUPT_LINE,
+                               ps->intr);
+                       pci_write_config_word(pd, PCI_COMMAND, ps->command);
+                       break;
+                       /* other header types not restored at present */
+               }
+       }
+}
+
+/*
+ * Put the powerbook to sleep.
+ */
+#define IRQ_ENABLE     ((unsigned int *)0xf3000024)
+#define MEM_CTRL       ((unsigned int *)0xf8000070)
+
+int __openfirmware powerbook_sleep(void)
+{
+       int ret, i, x;
+       static int save_backlight;
+       static unsigned int save_irqen;
+       unsigned long msr;
+       unsigned int hid0;
+       unsigned long p, wait;
+       struct adb_request sleep_req;
+
+       /* Notify device drivers */
+       ret = notifier_call_chain(&sleep_notifier_list, PBOOK_SLEEP, NULL);
+       if (ret & NOTIFY_STOP_MASK)
+               return -EBUSY;
+
+       /* Sync the disks. */
+       /* XXX It would be nice to have some way to ensure that
+        * nobody is dirtying any new buffers while we wait. */
+       fsync_dev(0);
+
+       /* Turn off the display backlight */
+       save_backlight = backlight_enabled;
+       if (save_backlight)
+               pmu_enable_backlight(0);
+
+       /* Give the disks a little time to actually finish writing */
+       for (wait = jiffies + (HZ/4); time_before(jiffies, wait); )
+               mb();
+
+       /* Disable all interrupts except pmu */
+       save_irqen = in_le32(IRQ_ENABLE);
+       for (i = 0; i < 32; ++i)
+               if (i != vias->intrs[0].line && (save_irqen & (1 << i)))
+                       disable_irq(i);
+       asm volatile("mtdec %0" : : "r" (0x7fffffff));
+
+       /* Save the state of PCI config space for some slots */
+       pbook_pci_save();
+
+       /* Set the memory controller to keep the memory refreshed
+          while we're asleep */
+       for (i = 0x403f; i >= 0x4000; --i) {
+               out_be32(MEM_CTRL, i);
+               do {
+                       x = (in_be32(MEM_CTRL) >> 16) & 0x3ff;
+               } while (x == 0);
+               if (x >= 0x100)
+                       break;
+       }
+
+       /* Ask the PMU to put us to sleep */
+       pmu_request(&sleep_req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+       while (!sleep_req.complete)
+               mb();
+       /* displacement-flush the L2 cache - necessary? */
+       for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000)
+               i = *(volatile int *)p;
+       asleep = 1;
+
+       /* Put the CPU into sleep mode */
+       asm volatile("mfspr %0,1008" : "=r" (hid0) :);
+       hid0 = (hid0 & ~(HID0_NAP | HID0_DOZE)) | HID0_SLEEP;
+       asm volatile("mtspr 1008,%0" : : "r" (hid0));
+       save_flags(msr);
+       msr |= MSR_POW | MSR_EE;
+       restore_flags(msr);
+       udelay(10);
+
+       /* OK, we're awake again, start restoring things */
+       out_be32(MEM_CTRL, 0x3f);
+       pbook_pci_restore();
+
+       /* wait for the PMU interrupt sequence to complete */
+       while (asleep)
+               mb();
+
+       /* reenable interrupts */
+       for (i = 0; i < 32; ++i)
+               if (i != vias->intrs[0].line && (save_irqen & (1 << i)))
+                       enable_irq(i);
+
+       /* Notify drivers */
+       notifier_call_chain(&sleep_notifier_list, PBOOK_WAKE, NULL);
+
+       /* reenable ADB autopoll */
+       pmu_adb_autopoll(adb_dev_map);
+
+       /* Turn on the screen backlight, if it was on before */
+       if (save_backlight)
+               pmu_enable_backlight(1);
+
+       /* Wait for the hard disk to spin up */
+
+       return 0;
+}
+
+/*
+ * Support for /dev/pmu device
+ */
+static int __openfirmware pmu_open(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+static ssize_t __openfirmware pmu_read(struct file *file, char *buf,
+                       size_t count, loff_t *ppos)
+{
+       return 0;
+}
+
+static ssize_t __openfirmware pmu_write(struct file *file, const char *buf,
+                        size_t count, loff_t *ppos)
+{
+       return 0;
+}
+
+/* Note: removed __openfirmware here since it causes link errors */
+static int /*__openfirmware*/ pmu_ioctl(struct inode * inode, struct file *filp,
+                    u_int cmd, u_long arg)
+{
+       int error;
+       __u32 value;
+
+       switch (cmd) {
+           case PMU_IOC_SLEEP:
+               return -ENOSYS;
+           case PMU_IOC_GET_BACKLIGHT:
+               return put_user(backlight_level, (__u32 *)arg);
+           case PMU_IOC_SET_BACKLIGHT:
+               error = get_user(value, (__u32 *)arg);
+               if (!error)
+                       pmu_set_brightness(value);
+               return error;
+           case PMU_IOC_GET_MODEL:
+               return put_user(pmu_kind, (__u32 *)arg);
+       }
+       return -EINVAL;
+}
+
+static struct file_operations pmu_device_fops = {
+       NULL,           /* no seek */
+       pmu_read,
+       pmu_write,
+       NULL,           /* no readdir */
+       NULL,           /* no poll yet */
+       pmu_ioctl,
+       NULL,           /* no mmap */
+       pmu_open,
+       NULL,           /* flush */
+       NULL            /* no release */
+};
+
+static struct miscdevice pmu_device = {
+       PMU_MINOR, "pmu", &pmu_device_fops
+};
+
+void pmu_device_init(void)
+{
+       if (via)
+               misc_register(&pmu_device);
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
index 099bd13007f9e31bc453e892959cc824a4ac0249..89d5fd599ba981af0f6a40bcb3ae6ed85d024115 100644 (file)
@@ -160,6 +160,16 @@ int __init ariadne_probe(struct net_device *dev)
            continue;
        }
        strcpy(z->name, "Ariadne Ethernet Card and Parallel Ports");
+
+       dev = init_etherdev(NULL, sizeof(struct ariadne_private));
+
+       if (dev == NULL) {
+           release_mem_region(base_addr, sizeof(struct Am79C960));
+           release_mem_region(ram_start, ARIADNE_RAM_SIZE);
+           return -ENOMEM;
+       }
+       memset(dev->priv, 0, sizeof(struct ariadne_private));
+
        dev->dev_addr[0] = 0x00;
        dev->dev_addr[1] = 0x60;
        dev->dev_addr[2] = 0x30;
@@ -171,16 +181,6 @@ int __init ariadne_probe(struct net_device *dev)
               dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
               dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
 
-       init_etherdev(dev, 0);
-
-       dev->priv = kmalloc(sizeof(struct ariadne_private), GFP_KERNEL);
-       if (dev->priv == NULL) {
-           release_mem_region(base_addr, sizeof(struct Am79C960));
-           release_mem_region(ram_start, ARIADNE_RAM_SIZE);
-           return -ENOMEM;
-       }
-       memset(dev->priv, 0, sizeof(struct ariadne_private));
-
        dev->base_addr = ZTWO_VADDR(base_addr);
        dev->mem_start = ZTWO_VADDR(ram_start);
        dev->mem_end = dev->mem_start+ARIADNE_RAM_SIZE;
index 922b284372003d93d6b614305e667e83cbcaa378..8e5511da17a6138e7b12bae9d32a1e65628561f0 100644 (file)
@@ -175,17 +175,16 @@ int __init hydra_probe(struct net_device *dev)
                }
                strcpy(z->name, "Hydra Ethernet Card");
 
+               dev = init_etherdev(NULL, sizeof(struct hydra_private));
+               memset(dev->priv, 0, sizeof(struct hydra_private));
+    
                for(j = 0; j < ETHER_ADDR_LEN; j++)
                        dev->dev_addr[j] = *((u8 *)ZTWO_VADDR(board + HYDRA_ADDRPROM + 2*j));
     
                printk("%s: hydra at 0x%08x, address %02x:%02x:%02x:%02x:%02x:%02x (hydra.c " HYDRA_VERSION ")\n",
                        dev->name, (int)board, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
                        dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
-               init_etherdev(dev, 0);
-    
-               dev->priv = kmalloc(sizeof(struct hydra_private), GFP_KERNEL);
-               memset(dev->priv, 0, sizeof(struct hydra_private));
-    
+
                dev->base_addr = ZTWO_VADDR(base_addr);
                dev->mem_start = ZTWO_VADDR(board);
                dev->mem_end = dev->mem_start+0x4000;
index 036ffdf2c49dd67c854bc524cea08add73aeec20..99cfa7e8c1fbe63e801d637d5f8639980f8dd4a8 100644 (file)
@@ -417,7 +417,9 @@ int mac_esp_detect(Scsi_Host_Template * tpnt)
                        esp->irq = IRQ_MAC_SCSI;
 
                        request_irq(IRQ_MAC_SCSI, esp_intr, 0, "Mac ESP SCSI", esp);
+#if 0  /* conflicts with IOP ADB */
                        request_irq(IRQ_MAC_SCSIDRQ, fake_drq, 0, "Mac ESP DRQ", esp);
+#endif
 
                        if (macintosh_config->scsi_type == MAC_SCSI_QUADRA) {
                                esp->cfreq = 16500000;
@@ -429,8 +431,9 @@ int mac_esp_detect(Scsi_Host_Template * tpnt)
                } else { /* chipnum == 1 */
 
                        esp->irq = IRQ_MAC_SCSIDRQ;
-
+#if 0  /* conflicts with IOP ADB */
                        request_irq(IRQ_MAC_SCSIDRQ, esp_intr, 0, "Mac ESP SCSI 2", esp);
+#endif
 
                        esp->cfreq = 25000000;
 
index 9e5bdf2b28ff8eccbfdae82e2087cb4beb73f514..11a58c478848fb5bf679d4f6ff3bed604d14ef4b 100644 (file)
@@ -78,7 +78,6 @@
 #define NDEBUG (NDEBUG_ABORT)
 #endif
 
-#define USE_WRAPPER
 #define RESET_BOOT
 #define DRIVER_SETUP
 
 #undef DRIVER_SETUP
 #endif
 
-/* 
- * Need to define this to make SCSI work on RBV machines; leave undefined
- * to enable interrupts a bit more on other machines 
- * Changes method of SCSI interrupt disable from software mask to VIA IER!
- * (don't know if that's essential)
- *
- * 990502 (jmt) - not needed (and won't work) on new irq architecture
- */
-/* #define RBV_HACK */
-
-#ifdef RBV_HACK
-#define        ENABLE_IRQ()    mac_turnon_irq( IRQ_MAC_SCSI ); 
-#define        DISABLE_IRQ()   mac_turnoff_irq( IRQ_MAC_SCSI );
-#else
 #define        ENABLE_IRQ()    mac_enable_irq( IRQ_MAC_SCSI ); 
 #define        DISABLE_IRQ()   mac_disable_irq( IRQ_MAC_SCSI );
-#endif
-
-#define mac_turnon_irq(x) mac_enable_irq(x)
-#define mac_turnoff_irq(x) mac_disable_irq(x)
 
-extern void via_scsi_clear(void);
-
-static void scsi_mac_intr(int irq, void *dummy, struct pt_regs *fp);
 #ifdef RESET_BOOT
 static void mac_scsi_reset_boot(struct Scsi_Host *instance);
 #endif
@@ -253,7 +231,7 @@ int macscsi_detect(Scsi_Host_Template * tpnt)
     if (macintosh_config->scsi_type != MAC_SCSI_OLD)
         return( 0 );
 
-    tpnt->proc_name = "Mac 5380 SCSI";
+    tpnt->proc_name = "mac5380";
 
     /* setup variables */
     tpnt->can_queue =
@@ -306,11 +284,7 @@ int macscsi_detect(Scsi_Host_Template * tpnt)
         ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0;
 
        if (instance->irq != IRQ_NONE)
-#ifdef USE_WRAPPER
-           if (request_irq(instance->irq, scsi_mac_intr, IRQ_FLG_SLOW, "MacSCSI-5380", NULL)) {
-#else
-           if (request_irq(instance->irq, macscsi_intr, IRQ_FLG_SLOW, "MacSCSI-5380", NULL)) {
-#endif
+           if (request_irq(instance->irq, NCR5380_intr, IRQ_FLG_SLOW, "ncr5380", NCR5380_intr)) {
                printk("scsi%d: IRQ%d not free, interrupts disabled\n",
                    instance->host_no, instance->irq);
                instance->irq = IRQ_NONE;
@@ -337,7 +311,7 @@ int macscsi_detect(Scsi_Host_Template * tpnt)
 int macscsi_release (struct Scsi_Host *shpnt)
 {
        if (shpnt->irq != IRQ_NONE)
-               free_irq (shpnt->irq, NULL);
+               free_irq (shpnt->irq, NCR5380_intr);
 
        return 0;
 }
@@ -362,7 +336,7 @@ static void mac_scsi_reset_boot(struct Scsi_Host *instance)
        printk( "Macintosh SCSI: resetting the SCSI bus..." );
 
        /* switch off SCSI IRQ - catch an interrupt without IRQ bit set else */
-               mac_turnoff_irq( IRQ_MAC_SCSI );
+               mac_disable_irq(IRQ_MAC_SCSI);
 
        /* get in phase */
        NCR5380_write( TARGET_COMMAND_REG,
@@ -380,7 +354,7 @@ static void mac_scsi_reset_boot(struct Scsi_Host *instance)
                barrier();
 
        /* switch on SCSI IRQ again */
-               mac_turnon_irq( IRQ_MAC_SCSI );
+               mac_enable_irq(IRQ_MAC_SCSI);
 
        printk( " done\n" );
 }
@@ -399,49 +373,6 @@ void restore_irq(struct pt_regs *regs)
        restore_flags(flags);
 }
 
-#ifdef USE_WRAPPER
-/*
- * SCSI interrupt wrapper - just to make sure it's the proper irq, and 
- * that we leave the handler in a clean state
- */
-
-static void scsi_mac_intr (int irq, void *dev_id, struct pt_regs *fp)
-{
-#ifndef RBV_HACK
-       unsigned long flags;
-#endif
-
-#ifdef RBV_HACK
-       mac_turnoff_irq( IRQ_MAC_SCSI );
-#else
-       mac_disable_irq( IRQ_MAC_SCSI );
-#endif
-
-       if ( irq == IRQ_MAC_SCSI ) {
-#ifndef RBV_HACK
-               save_flags(flags);
-               restore_irq(fp);
-#endif
-               NCR5380_intr (irq, dev_id, fp);
-#ifndef RBV_HACK
-               restore_flags(flags);
-#endif
-       }
-
-       /* To be sure the int is not masked */
-#ifdef RBV_HACK
-       mac_turnon_irq( IRQ_MAC_SCSI );
-#else
-       mac_enable_irq( IRQ_MAC_SCSI );
-#endif
-
-#if 1  /* ??? 0 worked */
-       /* Clear the IRQ */
-       via_scsi_clear();
-#endif
-}
-#endif
-
 /*
  * pseudo-DMA transfer functions, copied and modified from Russel King's
  * ARM 5380 driver (cumana_1)
@@ -720,11 +651,7 @@ void scsi_mac_polled (void)
                        printk("SCSI poll\n");
                        save_flags(flags);
                        cli();
-#ifdef USE_WRAPPER
-                       scsi_mac_intr(IRQ_MAC_SCSI, instance, NULL);
-#else
-                       macscsi_intr(IRQ_MAC_SCSI, instance, NULL);
-#endif
+                       NCR5380_intr(IRQ_MAC_SCSI, instance, NULL);
                        restore_flags(flags);
                }
 #if 0
index 71121b726fe857e33e58f48be24b6a549b1b76e5..fce0871f15c0b31b990116f103987ab164cdcb65 100644 (file)
@@ -40,7 +40,7 @@ int mvme16x_scsi_detect(Scsi_Host_Template *tpnt)
     if (called)
        return 0;
 
-    tpnt->proc_name = "MVME16x"
+    tpnt->proc_name = "MVME16x";
 
     options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT;
 
diff --git a/drivers/scsi/sun3_NCR5380.c b/drivers/scsi/sun3_NCR5380.c
new file mode 100644 (file)
index 0000000..5103868
--- /dev/null
@@ -0,0 +1,3012 @@
+/* sun3_NCR5380.c -- adapted from atari_NCR5380.c for the sun3 by 
+   Sam Creasey. */ 
+/* 
+ * NCR 5380 generic driver routines.  These should make it *trivial*
+ *     to implement 5380 SCSI drivers under Linux with a non-trantor
+ *     architecture.
+ *
+ *     Note that these routines also work with NR53c400 family chips.
+ *
+ * Copyright 1993, Drew Eckhardt
+ *     Visionary Computing 
+ *     (Unix and Linux consulting and custom programming)
+ *     drew@colorado.edu
+ *     +1 (303) 666-5836
+ *
+ * DISTRIBUTION RELEASE 6. 
+ *
+ * For more information, please consult 
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * ++roman: To port the 5380 driver to the Atari, I had to do some changes in
+ * this file, too:
+ *
+ *  - Some of the debug statements were incorrect (undefined variables and the
+ *    like). I fixed that.
+ *
+ *  - In information_transfer(), I think a #ifdef was wrong. Looking at the
+ *    possible DMA transfer size should also happen for REAL_DMA. I added this
+ *    in the #if statement.
+ *
+ *  - When using real DMA, information_transfer() should return in a DATAOUT
+ *    phase after starting the DMA. It has nothing more to do.
+ *
+ *  - The interrupt service routine should run main after end of DMA, too (not
+ *    only after RESELECTION interrupts). Additionally, it should _not_ test
+ *    for more interrupts after running main, since a DMA process may have
+ *    been started and interrupts are turned on now. The new int could happen
+ *    inside the execution of NCR5380_intr(), leading to recursive
+ *    calls.
+ *
+ *  - I've added a function merge_contiguous_buffers() that tries to
+ *    merge scatter-gather buffers that are located at contiguous
+ *    physical addresses and can be processed with the same DMA setup.
+ *    Since most scatter-gather operations work on a page (4K) of
+ *    4 buffers (1K), in more than 90% of all cases three interrupts and
+ *    DMA setup actions are saved.
+ *
+ * - I've deleted all the stuff for AUTOPROBE_IRQ, REAL_DMA_POLL, PSEUDO_DMA
+ *    and USLEEP, because these were messing up readability and will never be
+ *    needed for Atari SCSI.
+ * 
+ * - I've revised the NCR5380_main() calling scheme (relax the 'main_running'
+ *   stuff), and 'main' is executed in a bottom half if awoken by an
+ *   interrupt.
+ *
+ * - The code was quite cluttered up by "#if (NDEBUG & NDEBUG_*) printk..."
+ *   constructs. In my eyes, this made the source rather unreadable, so I
+ *   finally replaced that by the *_PRINTK() macros.
+ *
+ */
+
+/*
+ * Further development / testing that should be done : 
+ * 1.  Test linked command handling code after Eric is ready with 
+ *     the high level code.
+ */
+
+#if (NDEBUG & NDEBUG_LISTS)
+#define LIST(x,y) \
+  { printk("LINE:%d   Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); \
+    if ((x)==(y)) udelay(5); }
+#define REMOVE(w,x,y,z) \
+  { printk("LINE:%d   Removing: %p->%p  %p->%p \n", __LINE__, \
+          (void*)(w), (void*)(x), (void*)(y), (void*)(z)); \
+    if ((x)==(y)) udelay(5); }
+#else
+#define LIST(x,y)
+#define REMOVE(w,x,y,z)
+#endif
+
+#ifndef notyet
+#undef LINKED
+#endif
+
+/*
+ * Design
+ * Issues :
+ *
+ * The other Linux SCSI drivers were written when Linux was Intel PC-only,
+ * and specifically for each board rather than each chip.  This makes their
+ * adaptation to platforms like the Mac (Some of which use NCR5380's)
+ * more difficult than it has to be.
+ *
+ * Also, many of the SCSI drivers were written before the command queuing
+ * routines were implemented, meaning their implementations of queued 
+ * commands were hacked on rather than designed in from the start.
+ *
+ * When I designed the Linux SCSI drivers I figured that 
+ * while having two different SCSI boards in a system might be useful
+ * for debugging things, two of the same type wouldn't be used.
+ * Well, I was wrong and a number of users have mailed me about running
+ * multiple high-performance SCSI boards in a server.
+ *
+ * Finally, when I get questions from users, I have no idea what 
+ * revision of my driver they are running.
+ *
+ * This driver attempts to address these problems :
+ * This is a generic 5380 driver.  To use it on a different platform, 
+ * one simply writes appropriate system specific macros (ie, data
+ * transfer - some PC's will use the I/O bus, 68K's must use 
+ * memory mapped) and drops this file in their 'C' wrapper.
+ *
+ * As far as command queueing, two queues are maintained for 
+ * each 5380 in the system - commands that haven't been issued yet,
+ * and commands that are currently executing.  This means that an 
+ * unlimited number of commands may be queued, letting 
+ * more commands propagate from the higher driver levels giving higher 
+ * throughput.  Note that both I_T_L and I_T_L_Q nexuses are supported, 
+ * allowing multiple commands to propagate all the way to a SCSI-II device 
+ * while a command is already executing.
+ *
+ * To solve the multiple-boards-in-the-same-system problem, 
+ * there is a separate instance structure for each instance
+ * of a 5380 in the system.  So, multiple NCR5380 drivers will
+ * be able to coexist with appropriate changes to the high level
+ * SCSI code.  
+ *
+ * A NCR5380_PUBLIC_REVISION macro is provided, with the release
+ * number (updated for each public release) printed by the 
+ * NCR5380_print_options command, which should be called from the 
+ * wrapper detect function, so that I know what release of the driver
+ * users are using.
+ *
+ * Issues specific to the NCR5380 : 
+ *
+ * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead 
+ * piece of hardware that requires you to sit in a loop polling for 
+ * the REQ signal as long as you are connected.  Some devices are 
+ * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect 
+ * while doing long seek operations.
+ * 
+ * The workaround for this is to keep track of devices that have
+ * disconnected.  If the device hasn't disconnected, for commands that
+ * should disconnect, we do something like 
+ *
+ * while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
+ * 
+ * Some tweaking of N and M needs to be done.  An algorithm based 
+ * on "time to data" would give the best results as long as short time
+ * to datas (ie, on the same track) were considered, however these 
+ * broken devices are the exception rather than the rule and I'd rather
+ * spend my time optimizing for the normal case.
+ *
+ * Architecture :
+ *
+ * At the heart of the design is a coroutine, NCR5380_main,
+ * which is started when not running by the interrupt handler,
+ * timer, and queue command function.  It attempts to establish
+ * I_T_L or I_T_L_Q nexuses by removing the commands from the 
+ * issue queue and calling NCR5380_select() if a nexus 
+ * is not established. 
+ *
+ * Once a nexus is established, the NCR5380_information_transfer()
+ * phase goes through the various phases as instructed by the target.
+ * if the target goes into MSG IN and sends a DISCONNECT message,
+ * the command structure is placed into the per instance disconnected
+ * queue, and NCR5380_main tries to find more work.  If USLEEP
+ * was defined, and the target is idle for too long, the system
+ * will try to sleep.
+ *
+ * If a command has disconnected, eventually an interrupt will trigger,
+ * calling NCR5380_intr()  which will in turn call NCR5380_reselect
+ * to reestablish a nexus.  This will run main if necessary.
+ *
+ * On command termination, the done function will be called as 
+ * appropriate.
+ *
+ * SCSI pointers are maintained in the SCp field of SCSI command 
+ * structures, being initialized after the command is connected
+ * in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
+ * Note that in violation of the standard, an implicit SAVE POINTERS operation
+ * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
+ */
+
+/*
+ * Using this file :
+ * This file a skeleton Linux SCSI driver for the NCR 5380 series
+ * of chips.  To use it, you write an architecture specific functions 
+ * and macros and include this file in your driver.
+ *
+ * These macros control options : 
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ *     for commands that return with a CHECK CONDITION status. 
+ *
+ * LINKED - if defined, linked commands are supported.
+ *
+ * REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
+ *
+ * SUPPORT_TAGS - if defined, SCSI-2 tagged queuing is used where possible
+ *
+ * These macros MUST be defined :
+ * 
+ * NCR5380_read(register)  - read from the specified register
+ *
+ * NCR5380_write(register, value) - write to the specific register 
+ *
+ * Either real DMA *or* pseudo DMA may be implemented
+ * REAL functions : 
+ * NCR5380_REAL_DMA should be defined if real DMA is to be used.
+ * Note that the DMA setup functions should return the number of bytes 
+ *     that they were able to program the controller for.
+ *
+ * Also note that generic i386/PC versions of these macros are 
+ *     available as NCR5380_i386_dma_write_setup,
+ *     NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
+ *
+ * NCR5380_dma_write_setup(instance, src, count) - initialize
+ * NCR5380_dma_read_setup(instance, dst, count) - initialize
+ * NCR5380_dma_residual(instance); - residual count
+ *
+ * PSEUDO functions :
+ * NCR5380_pwrite(instance, src, count)
+ * NCR5380_pread(instance, dst, count);
+ *
+ * If nothing specific to this implementation needs doing (ie, with external
+ * hardware), you must also define 
+ *  
+ * NCR5380_queue_command
+ * NCR5380_reset
+ * NCR5380_abort
+ * NCR5380_proc_info
+ *
+ * to be the global entry points into the specific driver, ie 
+ * #define NCR5380_queue_command t128_queue_command.
+ *
+ * If this is not done, the routines will be defined as static functions
+ * with the NCR5380* names and the user must provide a globally
+ * accessible wrapper function.
+ *
+ * The generic driver is initialized by calling NCR5380_init(instance),
+ * after setting the appropriate host specific fields and ID.  If the 
+ * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
+ * possible) function may be used.  Before the specific driver initialization
+ * code finishes, NCR5380_print_options should be called.
+ */
+
+static struct Scsi_Host *first_instance = NULL;
+static Scsi_Host_Template *the_template = NULL;
+
+/* Macros ease life... :-) */
+#define        SETUP_HOSTDATA(in)                              \
+    struct NCR5380_hostdata *hostdata =                        \
+       (struct NCR5380_hostdata *)(in)->hostdata
+#define        HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata)
+
+#define        NEXT(cmd)       ((Scsi_Cmnd *)((cmd)->host_scribble))
+#define        NEXTADDR(cmd)   ((Scsi_Cmnd **)&((cmd)->host_scribble))
+
+#define        HOSTNO          instance->host_no
+#define        H_NO(cmd)       (cmd)->host->host_no
+
+#ifdef SUPPORT_TAGS
+
+/*
+ * Functions for handling tagged queuing
+ * =====================================
+ *
+ * ++roman (01/96): Now I've implemented SCSI-2 tagged queuing. Some notes:
+ *
+ * Using consecutive numbers for the tags is no good idea in my eyes. There
+ * could be wrong re-usings if the counter (8 bit!) wraps and some early
+ * command has been preempted for a long time. My solution: a bitfield for
+ * remembering used tags.
+ *
+ * There's also the problem that each target has a certain queue size, but we
+ * cannot know it in advance :-( We just see a QUEUE_FULL status being
+ * returned. So, in this case, the driver internal queue size assumption is
+ * reduced to the number of active tags if QUEUE_FULL is returned by the
+ * target. The command is returned to the mid-level, but with status changed
+ * to BUSY, since --as I've seen-- the mid-level can't handle QUEUE_FULL
+ * correctly.
+ *
+ * We're also not allowed running tagged commands as long as an untagged
+ * command is active. And REQUEST SENSE commands after a contingent allegiance
+ * condition _must_ be untagged. To keep track whether an untagged command has
+ * been issued, the host->busy array is still employed, as it is without
+ * support for tagged queuing.
+ *
+ * One could suspect that there are possible race conditions between
+ * is_lun_busy(), cmd_get_tag() and cmd_free_tag(). But I think this isn't the
+ * case: is_lun_busy() and cmd_get_tag() are both called from NCR5380_main(),
+ * which already guaranteed to be running at most once. It is also the only
+ * place where tags/LUNs are allocated. So no other allocation can slip
+ * between that pair, there could only happen a reselection, which can free a
+ * tag, but that doesn't hurt. Only the sequence in cmd_free_tag() becomes
+ * important: the tag bit must be cleared before 'nr_allocated' is decreased.
+ */
+
+/* -1 for TAG_NONE is not possible with unsigned char cmd->tag */
+#undef TAG_NONE
+#define TAG_NONE 0xff
+
+/* For the m68k, the number of bits in 'allocated' must be a multiple of 32! */
+#if (MAX_TAGS % 32) != 0
+#error "MAX_TAGS must be a multiple of 32!"
+#endif
+
+typedef struct {
+    char       allocated[MAX_TAGS/8];
+    int                nr_allocated;
+    int                queue_size;
+} TAG_ALLOC;
+
+static TAG_ALLOC TagAlloc[8][8]; /* 8 targets and 8 LUNs */
+
+
+static void __init init_tags( void )
+{
+    int target, lun;
+    TAG_ALLOC *ta;
+    
+    if (!setup_use_tagged_queuing)
+       return;
+    
+    for( target = 0; target < 8; ++target ) {
+       for( lun = 0; lun < 8; ++lun ) {
+           ta = &TagAlloc[target][lun];
+           memset( &ta->allocated, 0, MAX_TAGS/8 );
+           ta->nr_allocated = 0;
+           /* At the beginning, assume the maximum queue size we could
+            * support (MAX_TAGS). This value will be decreased if the target
+            * returns QUEUE_FULL status.
+            */
+           ta->queue_size = MAX_TAGS;
+       }
+    }
+}
+
+
+/* Check if we can issue a command to this LUN: First see if the LUN is marked
+ * busy by an untagged command. If the command should use tagged queuing, also
+ * check that there is a free tag and the target's queue won't overflow. This
+ * function should be called with interrupts disabled to avoid race
+ * conditions.
+ */ 
+
+static int is_lun_busy( Scsi_Cmnd *cmd, int should_be_tagged )
+{
+    SETUP_HOSTDATA(cmd->host);
+
+    if (hostdata->busy[cmd->target] & (1 << cmd->lun))
+       return( 1 );
+    if (!should_be_tagged ||
+       !setup_use_tagged_queuing || !cmd->device->tagged_supported)
+       return( 0 );
+    if (TagAlloc[cmd->target][cmd->lun].nr_allocated >=
+       TagAlloc[cmd->target][cmd->lun].queue_size ) {
+       TAG_PRINTK( "scsi%d: target %d lun %d: no free tags\n",
+                   H_NO(cmd), cmd->target, cmd->lun );
+       return( 1 );
+    }
+    return( 0 );
+}
+
+
+/* Allocate a tag for a command (there are no checks anymore, check_lun_busy()
+ * must be called before!), or reserve the LUN in 'busy' if the command is
+ * untagged.
+ */
+
+static void cmd_get_tag( Scsi_Cmnd *cmd, int should_be_tagged )
+{
+    SETUP_HOSTDATA(cmd->host);
+
+    /* If we or the target don't support tagged queuing, allocate the LUN for
+     * an untagged command.
+     */
+    if (!should_be_tagged ||
+       !setup_use_tagged_queuing || !cmd->device->tagged_supported) {
+       cmd->tag = TAG_NONE;
+       hostdata->busy[cmd->target] |= (1 << cmd->lun);
+       TAG_PRINTK( "scsi%d: target %d lun %d now allocated by untagged "
+                   "command\n", H_NO(cmd), cmd->target, cmd->lun );
+    }
+    else {
+       TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun];
+
+       cmd->tag = find_first_zero_bit( &ta->allocated, MAX_TAGS );
+       set_bit( cmd->tag, &ta->allocated );
+       ta->nr_allocated++;
+       TAG_PRINTK( "scsi%d: using tag %d for target %d lun %d "
+                   "(now %d tags in use)\n",
+                   H_NO(cmd), cmd->tag, cmd->target, cmd->lun,
+                   ta->nr_allocated );
+    }
+}
+
+
+/* Mark the tag of command 'cmd' as free, or in case of an untagged command,
+ * unlock the LUN.
+ */
+
+static void cmd_free_tag( Scsi_Cmnd *cmd )
+{
+    SETUP_HOSTDATA(cmd->host);
+
+    if (cmd->tag == TAG_NONE) {
+       hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+       TAG_PRINTK( "scsi%d: target %d lun %d untagged cmd finished\n",
+                   H_NO(cmd), cmd->target, cmd->lun );
+    }
+    else if (cmd->tag >= MAX_TAGS) {
+       printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n",
+               H_NO(cmd), cmd->tag );
+    }
+    else {
+       TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun];
+       clear_bit( cmd->tag, &ta->allocated );
+       ta->nr_allocated--;
+       TAG_PRINTK( "scsi%d: freed tag %d for target %d lun %d\n",
+                   H_NO(cmd), cmd->tag, cmd->target, cmd->lun );
+    }
+}
+
+
+static void free_all_tags( void )
+{
+    int target, lun;
+    TAG_ALLOC *ta;
+
+    if (!setup_use_tagged_queuing)
+       return;
+    
+    for( target = 0; target < 8; ++target ) {
+       for( lun = 0; lun < 8; ++lun ) {
+           ta = &TagAlloc[target][lun];
+           memset( &ta->allocated, 0, MAX_TAGS/8 );
+           ta->nr_allocated = 0;
+       }
+    }
+}
+
+#endif /* SUPPORT_TAGS */
+
+
+/*
+ * Function: void merge_contiguous_buffers( Scsi_Cmnd *cmd )
+ *
+ * Purpose: Try to merge several scatter-gather requests into one DMA
+ *    transfer. This is possible if the scatter buffers lie on
+ *    physical contiguous addresses.
+ *
+ * Parameters: Scsi_Cmnd *cmd
+ *    The command to work on. The first scatter buffer's data are
+ *    assumed to be already transfered into ptr/this_residual.
+ */
+
+static void merge_contiguous_buffers( Scsi_Cmnd *cmd )
+{
+    unsigned long endaddr;
+#if (NDEBUG & NDEBUG_MERGING)
+    unsigned long oldlen = cmd->SCp.this_residual;
+    int                  cnt = 1;
+#endif
+
+    for (endaddr = virt_to_phys(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1;
+        cmd->SCp.buffers_residual &&
+        virt_to_phys(cmd->SCp.buffer[1].address) == endaddr; ) {
+       
+       MER_PRINTK("VTOP(%p) == %08lx -> merging\n",
+                  cmd->SCp.buffer[1].address, endaddr);
+#if (NDEBUG & NDEBUG_MERGING)
+       ++cnt;
+#endif
+       ++cmd->SCp.buffer;
+       --cmd->SCp.buffers_residual;
+       cmd->SCp.this_residual += cmd->SCp.buffer->length;
+       endaddr += cmd->SCp.buffer->length;
+    }
+#if (NDEBUG & NDEBUG_MERGING)
+    if (oldlen != cmd->SCp.this_residual)
+       MER_PRINTK("merged %d buffers from %p, new length %08x\n",
+                  cnt, cmd->SCp.ptr, cmd->SCp.this_residual);
+#endif
+}
+
+/*
+ * Function : void initialize_SCp(Scsi_Cmnd *cmd)
+ *
+ * Purpose : initialize the saved data pointers for cmd to point to the 
+ *     start of the buffer.
+ *
+ * Inputs : cmd - Scsi_Cmnd structure to have pointers reset.
+ */
+
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd)
+{
+    /* 
+     * Initialize the Scsi Pointer field so that all of the commands in the 
+     * various queues are valid.
+     */
+
+    if (cmd->use_sg) {
+       cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
+       cmd->SCp.buffers_residual = cmd->use_sg - 1;
+       cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
+       cmd->SCp.this_residual = cmd->SCp.buffer->length;
+       /* ++roman: Try to merge some scatter-buffers if they are at
+        * contiguous physical addresses.
+        */
+       merge_contiguous_buffers( cmd );
+    } else {
+       cmd->SCp.buffer = NULL;
+       cmd->SCp.buffers_residual = 0;
+       cmd->SCp.ptr = (char *) cmd->request_buffer;
+       cmd->SCp.this_residual = cmd->request_bufflen;
+    }
+    
+}
+
+#include <linux/config.h>
+#include <linux/delay.h>
+
+#if 1
+static struct {
+    unsigned char mask;
+    const char * name;} 
+signals[] = {{ SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" }, 
+    { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD,  "CD" }, { SR_IO, "IO" }, 
+    { SR_SEL, "SEL" }, {0, NULL}}, 
+basrs[] = {{BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}},
+icrs[] = {{ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"},
+    {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"}, 
+    {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"}, 
+    {0, NULL}},
+mrs[] = {{MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"}, 
+    {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR, 
+    "MODE PARITY INTR"}, {MR_ENABLE_EOP_INTR,"MODE EOP INTR"},
+    {MR_MONITOR_BSY, "MODE MONITOR BSY"},
+    {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"}, 
+    {0, NULL}};
+
+/*
+ * Function : void NCR5380_print(struct Scsi_Host *instance)
+ *
+ * Purpose : print the SCSI bus signals for debugging purposes
+ *
+ * Input : instance - which NCR5380
+ */
+
+static void NCR5380_print(struct Scsi_Host *instance) {
+    unsigned char status, data, basr, mr, icr, i;
+    unsigned long flags;
+
+    save_flags(flags);
+    cli();
+    data = NCR5380_read(CURRENT_SCSI_DATA_REG);
+    status = NCR5380_read(STATUS_REG);
+    mr = NCR5380_read(MODE_REG);
+    icr = NCR5380_read(INITIATOR_COMMAND_REG);
+    basr = NCR5380_read(BUS_AND_STATUS_REG);
+    restore_flags(flags);
+    printk("STATUS_REG: %02x ", status);
+    for (i = 0; signals[i].mask ; ++i) 
+       if (status & signals[i].mask)
+           printk(",%s", signals[i].name);
+    printk("\nBASR: %02x ", basr);
+    for (i = 0; basrs[i].mask ; ++i) 
+       if (basr & basrs[i].mask)
+           printk(",%s", basrs[i].name);
+    printk("\nICR: %02x ", icr);
+    for (i = 0; icrs[i].mask; ++i) 
+       if (icr & icrs[i].mask)
+           printk(",%s", icrs[i].name);
+    printk("\nMODE: %02x ", mr);
+    for (i = 0; mrs[i].mask; ++i) 
+       if (mr & mrs[i].mask)
+           printk(",%s", mrs[i].name);
+    printk("\n");
+}
+
+static struct {
+    unsigned char value;
+    const char *name;
+} phases[] = {
+    {PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
+    {PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
+    {PHASE_UNKNOWN, "UNKNOWN"}};
+
+/* 
+ * Function : void NCR5380_print_phase(struct Scsi_Host *instance)
+ *
+ * Purpose : print the current SCSI phase for debugging purposes
+ *
+ * Input : instance - which NCR5380
+ */
+
+static void NCR5380_print_phase(struct Scsi_Host *instance)
+{
+    unsigned char status;
+    int i;
+
+    status = NCR5380_read(STATUS_REG);
+    if (!(status & SR_REQ)) 
+       printk(KERN_DEBUG "scsi%d: REQ not asserted, phase unknown.\n", HOSTNO);
+    else {
+       for (i = 0; (phases[i].value != PHASE_UNKNOWN) && 
+           (phases[i].value != (status & PHASE_MASK)); ++i); 
+       printk(KERN_DEBUG "scsi%d: phase %s\n", HOSTNO, phases[i].name);
+    }
+}
+
+#else /* !NDEBUG */
+
+/* dummies... */
+__inline__ void NCR5380_print(struct Scsi_Host *instance) { };
+__inline__ void NCR5380_print_phase(struct Scsi_Host *instance) { };
+
+#endif
+
+/*
+ * ++roman: New scheme of calling NCR5380_main()
+ * 
+ * If we're not in an interrupt, we can call our main directly, it cannot be
+ * already running. Else, we queue it on a task queue, if not 'main_running'
+ * tells us that a lower level is already executing it. This way,
+ * 'main_running' needs not be protected in a special way.
+ *
+ * queue_main() is a utility function for putting our main onto the task
+ * queue, if main_running is false. It should be called only from a
+ * interrupt or bottom half.
+ */
+
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+
+static volatile int main_running = 0;
+static struct tq_struct NCR5380_tqueue = {
+    NULL,              /* next */
+    0,                 /* sync */
+    (void (*)(void*))NCR5380_main,  /* routine, must have (void *) arg... */
+    NULL               /* data */
+};
+
+static __inline__ void queue_main(void)
+{
+    if (!main_running) {
+       /* If in interrupt and NCR5380_main() not already running,
+          queue it on the 'immediate' task queue, to be processed
+          immediately after the current interrupt processing has
+          finished. */
+       queue_task(&NCR5380_tqueue, &tq_immediate);
+       mark_bh(IMMEDIATE_BH);
+    }
+    /* else: nothing to do: the running NCR5380_main() will pick up
+       any newly queued command. */
+}
+
+
+static inline void NCR5380_all_init (void)
+{
+    static int done = 0;
+    if (!done) {
+       INI_PRINTK("scsi : NCR5380_all_init()\n");
+       done = 1;
+    }
+}
+
+/*
+ * Function : void NCR58380_print_options (struct Scsi_Host *instance)
+ *
+ * Purpose : called by probe code indicating the NCR5380 driver
+ *          options that were selected.
+ *
+ * Inputs : instance, pointer to this instance.  Unused.
+ */
+
+static void __init NCR5380_print_options (struct Scsi_Host *instance)
+{
+    printk(" generic options"
+#ifdef AUTOSENSE 
+    " AUTOSENSE"
+#endif
+#ifdef REAL_DMA
+    " REAL DMA"
+#endif
+#ifdef PARITY
+    " PARITY"
+#endif
+#ifdef SUPPORT_TAGS
+    " SCSI-2 TAGGED QUEUING"
+#endif
+    );
+    printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
+}
+
+/*
+ * Function : void NCR5380_print_status (struct Scsi_Host *instance)
+ *
+ * Purpose : print commands in the various queues, called from
+ *     NCR5380_abort and NCR5380_debug to aid debugging.
+ *
+ * Inputs : instance, pointer to this instance.  
+ */
+
+static void NCR5380_print_status (struct Scsi_Host *instance)
+{
+    char *pr_bfr;
+    char *start;
+    int len;
+
+    NCR_PRINT(NDEBUG_ANY);
+    NCR_PRINT_PHASE(NDEBUG_ANY);
+
+    pr_bfr = (char *) __get_free_page(GFP_ATOMIC);
+    if (!pr_bfr) {
+       printk("NCR5380_print_status: no memory for print buffer\n");
+       return;
+    }
+    len = NCR5380_proc_info(pr_bfr, &start, 0, PAGE_SIZE, HOSTNO, 0);
+    pr_bfr[len] = 0;
+    printk("\n%s\n", pr_bfr);
+    free_page((unsigned long) pr_bfr);
+}
+
+
+/******************************************/
+/*
+ * /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED]
+ *
+ * *buffer: I/O buffer
+ * **start: if inout == FALSE pointer into buffer where user read should start
+ * offset: current offset
+ * length: length of buffer
+ * hostno: Scsi_Host host_no
+ * inout: TRUE - user is writing; FALSE - user is reading
+ *
+ * Return the number of bytes read from or written
+*/
+
+#undef SPRINTF
+#define SPRINTF(fmt,args...) \
+  do { if (pos + strlen(fmt) + 20 /* slop */ < buffer + length) \
+        pos += sprintf(pos, fmt , ## args); } while(0)
+static
+char *lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length);
+
+#ifndef NCR5380_proc_info
+static
+#endif
+int NCR5380_proc_info (char *buffer, char **start, off_t offset,
+                      int length, int hostno, int inout)
+{
+    char *pos = buffer;
+    struct Scsi_Host *instance;
+    struct NCR5380_hostdata *hostdata;
+    Scsi_Cmnd *ptr;
+    unsigned long flags;
+    off_t begin = 0;
+#define check_offset()                         \
+    do {                                       \
+       if (pos - buffer < offset - begin) {    \
+           begin += pos - buffer;              \
+           pos = buffer;                       \
+       }                                       \
+    } while (0)
+
+    for (instance = first_instance; instance && HOSTNO != hostno;
+        instance = instance->next)
+       ;
+    if (!instance)
+       return(-ESRCH);
+    hostdata = (struct NCR5380_hostdata *)instance->hostdata;
+
+    if (inout) { /* Has data been written to the file ? */
+       return(-ENOSYS);  /* Currently this is a no-op */
+    }
+    SPRINTF("NCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE);
+    check_offset();
+    save_flags(flags);
+    cli();
+    SPRINTF("NCR5380: coroutine is%s running.\n", main_running ? "" : "n't");
+    check_offset();
+    if (!hostdata->connected)
+       SPRINTF("scsi%d: no currently connected command\n", HOSTNO);
+    else
+       pos = lprint_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected,
+                               pos, buffer, length);
+    SPRINTF("scsi%d: issue_queue\n", HOSTNO);
+    check_offset();
+    for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = NEXT(ptr)) {
+       pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
+       check_offset();
+    }
+
+    SPRINTF("scsi%d: disconnected_queue\n", HOSTNO);
+    check_offset();
+    for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
+        ptr = NEXT(ptr)) {
+       pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
+       check_offset();
+    }
+
+    restore_flags(flags);
+    *start = buffer + (offset - begin);
+    if (pos - buffer < offset - begin)
+       return 0;
+    else if (pos - buffer - (offset - begin) < length)
+       return pos - buffer - (offset - begin);
+    return length;
+}
+
+static char *
+lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length)
+{
+    int i, s;
+    unsigned char *command;
+    SPRINTF("scsi%d: destination target %d, lun %d\n",
+           H_NO(cmd), cmd->target, cmd->lun);
+    SPRINTF("        command = ");
+    command = cmd->cmnd;
+    SPRINTF("%2d (0x%02x)", command[0], command[0]);
+    for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
+       SPRINTF(" %02x", command[i]);
+    SPRINTF("\n");
+    return pos;
+}
+
+
+/* 
+ * Function : void NCR5380_init (struct Scsi_Host *instance)
+ *
+ * Purpose : initializes *instance and corresponding 5380 chip.
+ *
+ * Inputs : instance - instantiation of the 5380 driver.  
+ *
+ * Notes : I assume that the host, hostno, and id bits have been
+ *     set correctly.  I don't care about the irq and other fields. 
+ * 
+ */
+
+static void __init NCR5380_init (struct Scsi_Host *instance, int flags)
+{
+    int i;
+    SETUP_HOSTDATA(instance);
+
+    NCR5380_all_init();
+
+    hostdata->aborted = 0;
+    hostdata->id_mask = 1 << instance->this_id;
+    hostdata->id_higher_mask = 0;
+    for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
+       if (i > hostdata->id_mask)
+           hostdata->id_higher_mask |= i;
+    for (i = 0; i < 8; ++i)
+       hostdata->busy[i] = 0;
+#ifdef SUPPORT_TAGS
+    init_tags();
+#endif
+#if defined (REAL_DMA)
+    hostdata->dma_len = 0;
+#endif
+    hostdata->targets_present = 0;
+    hostdata->connected = NULL;
+    hostdata->issue_queue = NULL;
+    hostdata->disconnected_queue = NULL;
+    hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT;
+
+    if (!the_template) {
+       the_template = instance->hostt;
+       first_instance = instance;
+    }
+       
+
+#ifndef AUTOSENSE
+    if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1))
+        printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n"
+               "        without AUTOSENSE option, contingent allegiance conditions may\n"
+               "        be incorrectly cleared.\n", HOSTNO);
+#endif /* def AUTOSENSE */
+
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+    NCR5380_write(MODE_REG, MR_BASE);
+    NCR5380_write(TARGET_COMMAND_REG, 0);
+    NCR5380_write(SELECT_ENABLE_REG, 0);
+}
+
+/* 
+ * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd, 
+ *     void (*done)(Scsi_Cmnd *)) 
+ *
+ * Purpose :  enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ *     a pointer to the command descriptor.
+ * 
+ * Returns : 0
+ *
+ * Side effects : 
+ *      cmd is added to the per instance issue_queue, with minor 
+ *     twiddling done to the host specific fields of cmd.  If the 
+ *     main coroutine is not running, it is restarted.
+ *
+ */
+
+/* Only make static if a wrapper function is used */
+#ifndef NCR5380_queue_command
+static
+#endif
+int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+    SETUP_HOSTDATA(cmd->host);
+    Scsi_Cmnd *tmp;
+    unsigned long flags;
+    extern int update_timeout(Scsi_Cmnd * SCset, int timeout);
+
+#if (NDEBUG & NDEBUG_NO_WRITE)
+    switch (cmd->cmnd[0]) {
+    case WRITE_6:
+    case WRITE_10:
+       printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n",
+              H_NO(cmd));
+       cmd->result = (DID_ERROR << 16);
+       done(cmd);
+       return 0;
+    }
+#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
+
+
+#ifdef NCR5380_STATS
+# if 0
+    if (!hostdata->connected && !hostdata->issue_queue &&
+       !hostdata->disconnected_queue) {
+       hostdata->timebase = jiffies;
+    }
+# endif
+# ifdef NCR5380_STAT_LIMIT
+    if (cmd->request_bufflen > NCR5380_STAT_LIMIT)
+# endif
+       switch (cmd->cmnd[0])
+       {
+           case WRITE:
+           case WRITE_6:
+           case WRITE_10:
+               hostdata->time_write[cmd->target] -= (jiffies - hostdata->timebase);
+               hostdata->bytes_write[cmd->target] += cmd->request_bufflen;
+               hostdata->pendingw++;
+               break;
+           case READ:
+           case READ_6:
+           case READ_10:
+               hostdata->time_read[cmd->target] -= (jiffies - hostdata->timebase);
+               hostdata->bytes_read[cmd->target] += cmd->request_bufflen;
+               hostdata->pendingr++;
+               break;
+       }
+#endif
+
+    /* 
+     * We use the host_scribble field as a pointer to the next command  
+     * in a queue 
+     */
+
+    NEXT(cmd) = NULL;
+    cmd->scsi_done = done;
+
+    cmd->result = 0;
+
+
+    /* 
+     * Insert the cmd into the issue queue. Note that REQUEST SENSE 
+     * commands are added to the head of the queue since any command will
+     * clear the contingent allegiance condition that exists and the 
+     * sense data is only guaranteed to be valid while the condition exists.
+     */
+
+    save_flags(flags);
+    cli();
+    /* ++guenther: now that the issue queue is being set up, we can lock ST-DMA.
+     * Otherwise a running NCR5380_main may steal the lock.
+     * Lock before actually inserting due to fairness reasons explained in
+     * atari_scsi.c. If we insert first, then it's impossible for this driver
+     * to release the lock.
+     * Stop timer for this command while waiting for the lock, or timeouts
+     * may happen (and they really do), and it's no good if the command doesn't
+     * appear in any of the queues.
+     * ++roman: Just disabling the NCR interrupt isn't sufficient here,
+     * because also a timer int can trigger an abort or reset, which would
+     * alter queues and touch the lock.
+     */
+    if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+       LIST(cmd, hostdata->issue_queue);
+       NEXT(cmd) = hostdata->issue_queue;
+       hostdata->issue_queue = cmd;
+    } else {
+       for (tmp = (Scsi_Cmnd *)hostdata->issue_queue;
+            NEXT(tmp); tmp = NEXT(tmp))
+           ;
+       LIST(cmd, tmp);
+       NEXT(tmp) = cmd;
+    }
+
+    restore_flags(flags);
+
+    QU_PRINTK("scsi%d: command added to %s of queue\n", H_NO(cmd),
+             (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
+
+    /* If queue_command() is called from an interrupt (real one or bottom
+     * half), we let queue_main() do the job of taking care about main. If it
+     * is already running, this is a no-op, else main will be queued.
+     *
+     * If we're not in an interrupt, we can call NCR5380_main()
+     * unconditionally, because it cannot be already running.
+     */
+    if (in_interrupt() || ((flags >> 8) & 7) >= 6)
+       queue_main();
+    else
+       NCR5380_main();
+    return 0;
+}
+
+/*
+ * Function : NCR5380_main (void) 
+ *
+ * Purpose : NCR5380_main is a coroutine that runs as long as more work can 
+ *     be done on the NCR5380 host adapters in a system.  Both 
+ *     NCR5380_queue_command() and NCR5380_intr() will try to start it 
+ *     in case it is not running.
+ * 
+ * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should 
+ *  reenable them.  This prevents reentrancy and kernel stack overflow.
+ */    
+    
+static void NCR5380_main (void)
+{
+    Scsi_Cmnd *tmp, *prev;
+    struct Scsi_Host *instance = first_instance;
+    struct NCR5380_hostdata *hostdata = HOSTDATA(instance);
+    int done;
+    unsigned long flags;
+    
+    /*
+     * We run (with interrupts disabled) until we're sure that none of 
+     * the host adapters have anything that can be done, at which point 
+     * we set main_running to 0 and exit.
+     *
+     * Interrupts are enabled before doing various other internal 
+     * instructions, after we've decided that we need to run through
+     * the loop again.
+     *
+     * this should prevent any race conditions.
+     * 
+     * ++roman: Just disabling the NCR interrupt isn't sufficient here,
+     * because also a timer int can trigger an abort or reset, which can
+     * alter queues and touch the Falcon lock.
+     */
+
+    /* Tell int handlers main() is now already executing.  Note that
+       no races are possible here. If an int comes in before
+       'main_running' is set here, and queues/executes main via the
+       task queue, it doesn't do any harm, just this instance of main
+       won't find any work left to do. */
+    if (main_running)
+       return;
+    main_running = 1;
+
+    save_flags(flags);
+    do {
+       cli(); /* Freeze request queues */
+       done = 1;
+       
+       if (!hostdata->connected) {
+           MAIN_PRINTK( "scsi%d: not connected\n", HOSTNO );
+           /*
+            * Search through the issue_queue for a command destined
+            * for a target that's not busy.
+            */
+#if (NDEBUG & NDEBUG_LISTS)
+           for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL;
+                tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp))
+               ;
+           if ((tmp == prev) && tmp) printk(" LOOP\n");/* else printk("\n");*/
+#endif
+           for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, 
+                prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp) ) {
+
+#if (NDEBUG & NDEBUG_LISTS)
+               if (prev != tmp)
+                   printk("MAIN tmp=%p   target=%d   busy=%d lun=%d\n",
+                          tmp, tmp->target, hostdata->busy[tmp->target],
+                          tmp->lun);
+#endif
+               /*  When we find one, remove it from the issue queue. */
+               /* ++guenther: possible race with Falcon locking */
+               if (
+#ifdef SUPPORT_TAGS
+                   !is_lun_busy( tmp, tmp->cmnd[0] != REQUEST_SENSE)
+#else
+                   !(hostdata->busy[tmp->target] & (1 << tmp->lun))
+#endif
+                   ) {
+                   cli(); /* ++guenther: just to be sure, this must be atomic */
+                   if (prev) {
+                       REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
+                       NEXT(prev) = NEXT(tmp);
+                   } else {
+                       REMOVE(-1, hostdata->issue_queue, tmp, NEXT(tmp));
+                       hostdata->issue_queue = NEXT(tmp);
+                   }
+                   NEXT(tmp) = NULL;
+                   
+                   /* reenable interrupts after finding one */
+                   restore_flags(flags);
+                   
+                   /* 
+                    * Attempt to establish an I_T_L nexus here. 
+                    * On success, instance->hostdata->connected is set.
+                    * On failure, we must add the command back to the
+                    *   issue queue so we can keep trying.     
+                    */
+                   MAIN_PRINTK("scsi%d: main(): command for target %d "
+                               "lun %d removed from issue_queue\n",
+                               HOSTNO, tmp->target, tmp->lun);
+                   /* 
+                    * REQUEST SENSE commands are issued without tagged
+                    * queueing, even on SCSI-II devices because the 
+                    * contingent allegiance condition exists for the 
+                    * entire unit.
+                    */
+                   /* ++roman: ...and the standard also requires that
+                    * REQUEST SENSE command are untagged.
+                    */
+                   
+#ifdef SUPPORT_TAGS
+                   cmd_get_tag( tmp, tmp->cmnd[0] != REQUEST_SENSE );
+#endif
+                   if (!NCR5380_select(instance, tmp, 
+                           (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : 
+                           TAG_NEXT)) {
+                       break;
+                   } else {
+                       cli();
+                       LIST(tmp, hostdata->issue_queue);
+                       NEXT(tmp) = hostdata->issue_queue;
+                       hostdata->issue_queue = tmp;
+#ifdef SUPPORT_TAGS
+                       cmd_free_tag( tmp );
+#endif
+                       restore_flags(flags);
+                       MAIN_PRINTK("scsi%d: main(): select() failed, "
+                                   "returned to issue_queue\n", HOSTNO);
+                       if (hostdata->connected)
+                           break;
+                   }
+               } /* if target/lun/target queue is not busy */
+           } /* for issue_queue */
+       } /* if (!hostdata->connected) */
+       if (hostdata->connected 
+#ifdef REAL_DMA
+           && !hostdata->dma_len
+#endif
+           ) {
+           restore_flags(flags);
+           MAIN_PRINTK("scsi%d: main: performing information transfer\n",
+                       HOSTNO);
+           NCR5380_information_transfer(instance);
+           MAIN_PRINTK("scsi%d: main: done set false\n", HOSTNO);
+           done = 0;
+       }
+    } while (!done);
+
+    /* Better allow ints _after_ 'main_running' has been cleared, else
+       an interrupt could believe we'll pick up the work it left for
+       us, but we won't see it anymore here... */
+    main_running = 0;
+    restore_flags(flags);
+}
+
+
+#ifdef REAL_DMA
+/*
+ * Function : void NCR5380_dma_complete (struct Scsi_Host *instance)
+ *
+ * Purpose : Called by interrupt handler when DMA finishes or a phase
+ *     mismatch occurs (which would finish the DMA transfer).  
+ *
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ */
+
+static void NCR5380_dma_complete( struct Scsi_Host *instance )
+{
+    SETUP_HOSTDATA(instance);
+    int           transfered;
+    unsigned char **data;
+    volatile int  *count;
+
+    if (!hostdata->connected) {
+       printk(KERN_WARNING "scsi%d: received end of DMA interrupt with "
+              "no connected cmd\n", HOSTNO);
+       return;
+    }
+
+    DMA_PRINTK("scsi%d: real DMA transfer complete, basr 0x%X, sr 0x%X\n",
+              HOSTNO, NCR5380_read(BUS_AND_STATUS_REG),
+              NCR5380_read(STATUS_REG));
+
+    if((sun3scsi_dma_finish())) {
+           printk("scsi%d: overrun in UDC counter -- not prepared to deal with this!\n", HOSTNO);
+           printk("please e-mail sammy@oh.verio.com with a description of how this\n");
+           printk("error was produced.\n");
+           machine_halt();
+    }
+
+    /* make sure we're not stuck in a data phase */
+    if((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH |
+                                           BASR_ACK)) ==
+       (BASR_PHASE_MATCH | BASR_ACK)) {
+           printk("scsi%d: BASR %02x\n", HOSTNO, NCR5380_read(BUS_AND_STATUS_REG));
+           printk("scsi%d: bus stuck in data phase -- probably a
+ single byte overrun!\n", HOSTNO); 
+           printk("not prepared for this error!\n");
+           printk("please e-mail sammy@oh.verio.com with a description of how this\n");
+           printk("error was produced.\n");
+           machine_halt();
+    }
+
+
+
+    (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+    NCR5380_write(MODE_REG, MR_BASE);
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+    transfered = hostdata->dma_len - NCR5380_dma_residual(instance);
+    hostdata->dma_len = 0;
+
+    data = (unsigned char **) &(hostdata->connected->SCp.ptr);
+    count = &(hostdata->connected->SCp.this_residual);
+    *data += transfered;
+    *count -= transfered;
+
+}
+#endif /* REAL_DMA */
+
+
+/*
+ * Function : void NCR5380_intr (int irq)
+ * 
+ * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
+ *     from the disconnected queue, and restarting NCR5380_main() 
+ *     as required.
+ *
+ * Inputs : int irq, irq that caused this interrupt.
+ *
+ */
+
+static void NCR5380_intr (int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct Scsi_Host *instance = first_instance;
+    int done = 1;
+    unsigned char basr;
+
+    INT_PRINTK("scsi%d: NCR5380 irq triggered\n", HOSTNO);
+
+    /* Look for pending interrupts */
+    basr = NCR5380_read(BUS_AND_STATUS_REG);
+    INT_PRINTK("scsi%d: BASR=%02x\n", HOSTNO, basr);
+    /* dispatch to appropriate routine if found and done=0 */
+    if (basr & BASR_IRQ) {
+       NCR_PRINT(NDEBUG_INTR);
+       if ((NCR5380_read(STATUS_REG) & (SR_SEL|SR_IO)) == (SR_SEL|SR_IO)) {
+           done = 0;
+           ENABLE_IRQ();
+           INT_PRINTK("scsi%d: SEL interrupt\n", HOSTNO);
+           NCR5380_reselect(instance);
+           (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+       }
+       else if (basr & BASR_PARITY_ERROR) {
+           INT_PRINTK("scsi%d: PARITY interrupt\n", HOSTNO);
+           (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+       }
+       else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) {
+           INT_PRINTK("scsi%d: RESET interrupt\n", HOSTNO);
+           (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+       }
+       else {
+           /*  
+            * The rest of the interrupt conditions can occur only during a
+            * DMA transfer
+            */
+
+#if defined(REAL_DMA)
+           /*
+            * We should only get PHASE MISMATCH and EOP interrupts if we have
+            * DMA enabled, so do a sanity check based on the current setting
+            * of the MODE register.
+            */
+
+           if ((NCR5380_read(MODE_REG) & MR_DMA_MODE) &&
+               ((basr & BASR_END_DMA_TRANSFER) || 
+                !(basr & BASR_PHASE_MATCH))) {
+                   
+               INT_PRINTK("scsi%d: PHASE MISM or EOP interrupt\n", HOSTNO);
+               NCR5380_dma_complete( instance );
+               done = 0;
+               ENABLE_IRQ();
+           } else
+#endif /* REAL_DMA */
+           {
+/* MS: Ignore unknown phase mismatch interrupts (caused by EOP interrupt) */
+               if (basr & BASR_PHASE_MATCH)
+                   printk(KERN_NOTICE "scsi%d: unknown interrupt, "
+                          "BASR 0x%x, MR 0x%x, SR 0x%x\n",
+                          HOSTNO, basr, NCR5380_read(MODE_REG),
+                          NCR5380_read(STATUS_REG));
+               (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+           }
+       } /* if !(SELECTION || PARITY) */
+    } /* BASR & IRQ */
+    else {
+
+       printk(KERN_NOTICE "scsi%d: interrupt without IRQ bit set in BASR, "
+              "BASR 0x%X, MR 0x%X, SR 0x%x\n", HOSTNO, basr,
+              NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
+       (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+    }
+    
+    if (!done) {
+       INT_PRINTK("scsi%d: in int routine, calling main\n", HOSTNO);
+       /* Put a call to NCR5380_main() on the queue... */
+       queue_main();
+    }
+}
+
+#ifdef NCR5380_STATS
+static void collect_stats(struct NCR5380_hostdata* hostdata, Scsi_Cmnd* cmd)
+{
+# ifdef NCR5380_STAT_LIMIT
+    if (cmd->request_bufflen > NCR5380_STAT_LIMIT)
+# endif
+       switch (cmd->cmnd[0])
+       {
+           case WRITE:
+           case WRITE_6:
+           case WRITE_10:
+               hostdata->time_write[cmd->target] += (jiffies - hostdata->timebase);
+               /*hostdata->bytes_write[cmd->target] += cmd->request_bufflen;*/
+               hostdata->pendingw--;
+               break;
+           case READ:
+           case READ_6:
+           case READ_10:
+               hostdata->time_read[cmd->target] += (jiffies - hostdata->timebase);
+               /*hostdata->bytes_read[cmd->target] += cmd->request_bufflen;*/
+               hostdata->pendingr--;
+               break;
+       }
+}
+#endif
+
+/* 
+ * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, 
+ *     int tag);
+ *
+ * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
+ *     including ARBITRATION, SELECTION, and initial message out for 
+ *     IDENTIFY and queue messages. 
+ *
+ * Inputs : instance - instantiation of the 5380 driver on which this 
+ *     target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for 
+ *     new tag, TAG_NONE for untagged queueing, otherwise set to the tag for 
+ *     the command that is presently connected.
+ * 
+ * Returns : -1 if selection could not execute for some reason,
+ *     0 if selection succeeded or failed because the target 
+ *     did not respond.
+ *
+ * Side effects : 
+ *     If bus busy, arbitration failed, etc, NCR5380_select() will exit 
+ *             with registers as they should have been on entry - ie
+ *             SELECT_ENABLE will be set appropriately, the NCR5380
+ *             will cease to drive any SCSI bus signals.
+ *
+ *     If successful : I_T_L or I_T_L_Q nexus will be established, 
+ *             instance->connected will be set to cmd.  
+ *             SELECT interrupt will be disabled.
+ *
+ *     If failed (no target) : cmd->scsi_done() will be called, and the 
+ *             cmd->result host byte set to DID_BAD_TARGET.
+ */
+
+static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
+{
+    SETUP_HOSTDATA(instance);
+    unsigned char tmp[3], phase;
+    unsigned char *data;
+    int len;
+    unsigned long timeout;
+    unsigned long flags;
+
+    hostdata->restart_select = 0;
+    NCR_PRINT(NDEBUG_ARBITRATION);
+    ARB_PRINTK("scsi%d: starting arbitration, id = %d\n", HOSTNO,
+              instance->this_id);
+
+    /* 
+     * Set the phase bits to 0, otherwise the NCR5380 won't drive the 
+     * data bus during SELECTION.
+     */
+
+    save_flags(flags);
+    cli();
+    if (hostdata->connected) {
+       restore_flags(flags);
+       return -1;
+    }
+    NCR5380_write(TARGET_COMMAND_REG, 0);
+
+
+    /* 
+     * Start arbitration.
+     */
+    
+    NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
+    NCR5380_write(MODE_REG, MR_ARBITRATE);
+
+    restore_flags(flags);
+
+    /* Wait for arbitration logic to complete */
+#if NCR_TIMEOUT
+    {
+      unsigned long timeout = jiffies + 2*NCR_TIMEOUT;
+
+      while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
+          && time_before(jiffies, timeout) && !hostdata->connected)
+       ;
+      if (time_after_eq(jiffies, timeout))
+      {
+       printk("scsi : arbitration timeout at %d\n", __LINE__);
+       NCR5380_write(MODE_REG, MR_BASE);
+       NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+       return -1;
+      }
+    }
+#else /* NCR_TIMEOUT */
+    while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
+        && !hostdata->connected);
+#endif
+
+    ARB_PRINTK("scsi%d: arbitration complete\n", HOSTNO);
+
+    if (hostdata->connected) {
+       NCR5380_write(MODE_REG, MR_BASE); 
+       return -1;
+    }
+    /* 
+     * The arbitration delay is 2.2us, but this is a minimum and there is 
+     * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate
+     * the integral nature of udelay().
+     *
+     */
+
+    udelay(3);
+
+    /* Check for lost arbitration */
+    if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+       (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) ||
+       (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+       hostdata->connected) {
+       NCR5380_write(MODE_REG, MR_BASE); 
+       ARB_PRINTK("scsi%d: lost arbitration, deasserting MR_ARBITRATE\n",
+                  HOSTNO);
+       return -1;
+    }
+
+     /* after/during arbitration, BSY should be asserted.
+       IBM DPES-31080 Version S31Q works now */
+     /* Tnx to Thomas_Roesch@m2.maus.de for finding this! (Roman) */
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL |
+                                        ICR_ASSERT_BSY ) ;
+    
+    if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+       hostdata->connected) {
+       NCR5380_write(MODE_REG, MR_BASE);
+       NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+       ARB_PRINTK("scsi%d: lost arbitration, deasserting ICR_ASSERT_SEL\n",
+                  HOSTNO);
+       return -1;
+    }
+
+    /* 
+     * Again, bus clear + bus settle time is 1.2us, however, this is 
+     * a minimum so we'll udelay ceil(1.2)
+     */
+
+#ifdef CONFIG_ATARI_SCSI_TOSHIBA_DELAY
+    /* ++roman: But some targets (see above :-) seem to need a bit more... */
+    udelay(15);
+#else
+    udelay(2);
+#endif
+    
+    if (hostdata->connected) {
+       NCR5380_write(MODE_REG, MR_BASE);
+       NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+       return -1;
+    }
+
+    ARB_PRINTK("scsi%d: won arbitration\n", HOSTNO);
+
+    /* 
+     * Now that we have won arbitration, start Selection process, asserting 
+     * the host and target ID's on the SCSI bus.
+     */
+
+    NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->target)));
+
+    /* 
+     * Raise ATN while SEL is true before BSY goes false from arbitration,
+     * since this is the only way to guarantee that we'll get a MESSAGE OUT
+     * phase immediately after selection.
+     */
+
+    NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY | 
+       ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL ));
+    NCR5380_write(MODE_REG, MR_BASE);
+
+    /* 
+     * Reselect interrupts must be turned off prior to the dropping of BSY,
+     * otherwise we will trigger an interrupt.
+     */
+
+    if (hostdata->connected) {
+       NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+       return -1;
+    }
+
+    NCR5380_write(SELECT_ENABLE_REG, 0);
+
+    /*
+     * The initiator shall then wait at least two deskew delays and release 
+     * the BSY signal.
+     */
+    udelay(1);        /* wingel -- wait two bus deskew delay >2*45ns */
+
+    /* Reset BSY */
+    NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA | 
+       ICR_ASSERT_ATN | ICR_ASSERT_SEL));
+
+    /* 
+     * Something weird happens when we cease to drive BSY - looks
+     * like the board/chip is letting us do another read before the 
+     * appropriate propagation delay has expired, and we're confusing
+     * a BSY signal from ourselves as the target's response to SELECTION.
+     *
+     * A small delay (the 'C++' frontend breaks the pipeline with an
+     * unnecessary jump, making it work on my 386-33/Trantor T128, the
+     * tighter 'C' code breaks and requires this) solves the problem - 
+     * the 1 us delay is arbitrary, and only used because this delay will 
+     * be the same on other platforms and since it works here, it should 
+     * work there.
+     *
+     * wingel suggests that this could be due to failing to wait
+     * one deskew delay.
+     */
+
+    udelay(1);
+
+    SEL_PRINTK("scsi%d: selecting target %d\n", HOSTNO, cmd->target);
+
+    /* 
+     * The SCSI specification calls for a 250 ms timeout for the actual 
+     * selection.
+     */
+
+    timeout = jiffies + 25; 
+
+    /* 
+     * XXX very interesting - we're seeing a bounce where the BSY we 
+     * asserted is being reflected / still asserted (propagation delay?)
+     * and it's detecting as true.  Sigh.
+     */
+
+#if 0
+    /* ++roman: If a target conformed to the SCSI standard, it wouldn't assert
+     * IO while SEL is true. But again, there are some disks out the in the
+     * world that do that nevertheless. (Somebody claimed that this announces
+     * reselection capability of the target.) So we better skip that test and
+     * only wait for BSY... (Famous german words: Der Klügere gibt nach :-)
+     */
+
+    while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & 
+       (SR_BSY | SR_IO)));
+
+    if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == 
+           (SR_SEL | SR_IO)) {
+           NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+           NCR5380_reselect(instance);
+           printk (KERN_ERR "scsi%d: reselection after won arbitration?\n",
+                   HOSTNO);
+           NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+           return -1;
+    }
+#else
+    while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY));
+#endif
+
+    /* 
+     * No less than two deskew delays after the initiator detects the 
+     * BSY signal is true, it shall release the SEL signal and may 
+     * change the DATA BUS.                                     -wingel
+     */
+
+    udelay(1);
+
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+    if (!(NCR5380_read(STATUS_REG) & SR_BSY)) {
+       NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+       if (hostdata->targets_present & (1 << cmd->target)) {
+           printk(KERN_ERR "scsi%d: weirdness\n", HOSTNO);
+           if (hostdata->restart_select)
+               printk(KERN_NOTICE "\trestart select\n");
+           NCR_PRINT(NDEBUG_ANY);
+           NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+           return -1;
+       }
+       cmd->result = DID_BAD_TARGET << 16;
+#ifdef NCR5380_STATS
+       collect_stats(hostdata, cmd);
+#endif
+#ifdef SUPPORT_TAGS
+       cmd_free_tag( cmd );
+#endif
+       cmd->scsi_done(cmd);
+       NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+       SEL_PRINTK("scsi%d: target did not respond within 250ms\n", HOSTNO);
+       NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+       return 0;
+    } 
+
+    hostdata->targets_present |= (1 << cmd->target);
+
+    /*
+     * Since we followed the SCSI spec, and raised ATN while SEL 
+     * was true but before BSY was false during selection, the information
+     * transfer phase should be a MESSAGE OUT phase so that we can send the
+     * IDENTIFY message.
+     * 
+     * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG
+     * message (2 bytes) with a tag ID that we increment with every command
+     * until it wraps back to 0.
+     *
+     * XXX - it turns out that there are some broken SCSI-II devices,
+     *      which claim to support tagged queuing but fail when more than
+     *      some number of commands are issued at once.
+     */
+
+    /* Wait for start of REQ/ACK handshake */
+    while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+
+    SEL_PRINTK("scsi%d: target %d selected, going into MESSAGE OUT phase.\n",
+              HOSTNO, cmd->target);
+    tmp[0] = IDENTIFY(1, cmd->lun);
+
+#ifdef SUPPORT_TAGS
+    if (cmd->tag != TAG_NONE) {
+       tmp[1] = hostdata->last_message = SIMPLE_QUEUE_TAG;
+       tmp[2] = cmd->tag;
+       len = 3;
+    } else 
+       len = 1;
+#else
+    len = 1;
+    cmd->tag=0;
+#endif /* SUPPORT_TAGS */
+
+    /* Send message(s) */
+    data = tmp;
+    phase = PHASE_MSGOUT;
+    NCR5380_transfer_pio(instance, &phase, &len, &data);
+    SEL_PRINTK("scsi%d: nexus established.\n", HOSTNO);
+    /* XXX need to handle errors here */
+    hostdata->connected = cmd;
+#ifndef SUPPORT_TAGS
+    hostdata->busy[cmd->target] |= (1 << cmd->lun);
+#endif    
+
+    initialize_SCp(cmd);
+
+
+    return 0;
+}
+
+/* 
+ * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance, 
+ *      unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using polled I/O
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to 
+ *     what phase is expected, *count - pointer to number of 
+ *     bytes to transfer, **data - pointer to data pointer.
+ * 
+ * Returns : -1 when different phase is entered without transferring
+ *     maximum number of bytes, 0 if all bytes are transfered or exit
+ *     is in same phase.
+ *
+ *     Also, *phase, *count, *data are modified in place.
+ *
+ * XXX Note : handling for bus free may be useful.
+ */
+
+/*
+ * Note : this code is not as quick as it could be, however it 
+ * IS 100% reliable, and for the actual data transfer where speed
+ * counts, we will always do a pseudo DMA or DMA transfer.
+ */
+
+static int NCR5380_transfer_pio( struct Scsi_Host *instance, 
+                                unsigned char *phase, int *count,
+                                unsigned char **data)
+{
+    register unsigned char p = *phase, tmp;
+    register int c = *count;
+    register unsigned char *d = *data;
+
+    /* 
+     * The NCR5380 chip will only drive the SCSI bus when the 
+     * phase specified in the appropriate bits of the TARGET COMMAND
+     * REGISTER match the STATUS REGISTER
+     */
+
+    NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+
+    do {
+       /* 
+        * Wait for assertion of REQ, after which the phase bits will be 
+        * valid 
+        */
+       while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ));
+
+       HSH_PRINTK("scsi%d: REQ detected\n", HOSTNO);
+
+       /* Check for phase mismatch */  
+       if ((tmp & PHASE_MASK) != p) {
+           PIO_PRINTK("scsi%d: phase mismatch\n", HOSTNO);
+           NCR_PRINT_PHASE(NDEBUG_PIO);
+           break;
+       }
+
+       /* Do actual transfer from SCSI bus to / from memory */
+       if (!(p & SR_IO)) 
+           NCR5380_write(OUTPUT_DATA_REG, *d);
+       else 
+           *d = NCR5380_read(CURRENT_SCSI_DATA_REG);
+
+       ++d;
+
+       /* 
+        * The SCSI standard suggests that in MSGOUT phase, the initiator
+        * should drop ATN on the last byte of the message phase
+        * after REQ has been asserted for the handshake but before
+        * the initiator raises ACK.
+        */
+
+       if (!(p & SR_IO)) {
+           if (!((p & SR_MSG) && c > 1)) {
+               NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
+                   ICR_ASSERT_DATA);
+               NCR_PRINT(NDEBUG_PIO);
+               NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
+                       ICR_ASSERT_DATA | ICR_ASSERT_ACK);
+           } else {
+               NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+                   ICR_ASSERT_DATA | ICR_ASSERT_ATN);
+               NCR_PRINT(NDEBUG_PIO);
+               NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
+                   ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
+           }
+       } else {
+           NCR_PRINT(NDEBUG_PIO);
+           NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
+       }
+
+       while (NCR5380_read(STATUS_REG) & SR_REQ);
+
+       HSH_PRINTK("scsi%d: req false, handshake complete\n", HOSTNO);
+
+/*
+ * We have several special cases to consider during REQ/ACK handshaking : 
+ * 1.  We were in MSGOUT phase, and we are on the last byte of the 
+ *     message.  ATN must be dropped as ACK is dropped.
+ *
+ * 2.  We are in a MSGIN phase, and we are on the last byte of the  
+ *     message.  We must exit with ACK asserted, so that the calling
+ *     code may raise ATN before dropping ACK to reject the message.
+ *
+ * 3.  ACK and ATN are clear and the target may proceed as normal.
+ */
+       if (!(p == PHASE_MSGIN && c == 1)) {  
+           if (p == PHASE_MSGOUT && c > 1)
+               NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+           else
+               NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+       } 
+    } while (--c);
+
+    PIO_PRINTK("scsi%d: residual %d\n", HOSTNO, c);
+
+    *count = c;
+    *data = d;
+    tmp = NCR5380_read(STATUS_REG);
+    /* The phase read from the bus is valid if either REQ is (already)
+     * asserted or if ACK hasn't been released yet. The latter is the case if
+     * we're in MSGIN and all wanted bytes have been received. */
+    if ((tmp & SR_REQ) || (p == PHASE_MSGIN && c == 0))
+       *phase = tmp & PHASE_MASK;
+    else 
+       *phase = PHASE_UNKNOWN;
+
+    if (!c || (*phase == p))
+       return 0;
+    else 
+       return -1;
+}
+
+/*
+ * Function : do_abort (Scsi_Host *host)
+ * 
+ * Purpose : abort the currently established nexus.  Should only be 
+ *     called from a routine which can drop into a 
+ * 
+ * Returns : 0 on success, -1 on failure.
+ */
+
+static int do_abort (struct Scsi_Host *host) 
+{
+    unsigned char tmp, *msgptr, phase;
+    int len;
+
+    /* Request message out phase */
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+    /* 
+     * Wait for the target to indicate a valid phase by asserting 
+     * REQ.  Once this happens, we'll have either a MSGOUT phase 
+     * and can immediately send the ABORT message, or we'll have some 
+     * other phase and will have to source/sink data.
+     * 
+     * We really don't care what value was on the bus or what value
+     * the target sees, so we just handshake.
+     */
+    
+    while (!(tmp = NCR5380_read(STATUS_REG)) & SR_REQ);
+
+    NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+    if ((tmp & PHASE_MASK) != PHASE_MSGOUT) {
+       NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | 
+                     ICR_ASSERT_ACK);
+       while (NCR5380_read(STATUS_REG) & SR_REQ);
+       NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+    }
+   
+    tmp = ABORT;
+    msgptr = &tmp;
+    len = 1;
+    phase = PHASE_MSGOUT;
+    NCR5380_transfer_pio (host, &phase, &len, &msgptr);
+
+    /*
+     * If we got here, and the command completed successfully,
+     * we're about to go into bus free state.
+     */
+
+    return len ? -1 : 0;
+}
+
+#if defined(REAL_DMA)
+/* 
+ * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance, 
+ *      unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using either real
+ *     or pseudo DMA.
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to 
+ *     what phase is expected, *count - pointer to number of 
+ *     bytes to transfer, **data - pointer to data pointer.
+ * 
+ * Returns : -1 when different phase is entered without transferring
+ *     maximum number of bytes, 0 if all bytes or transfered or exit
+ *     is in same phase.
+ *
+ *     Also, *phase, *count, *data are modified in place.
+ *
+ */
+
+
+static int NCR5380_transfer_dma( struct Scsi_Host *instance, 
+                                unsigned char *phase, int *count,
+                                unsigned char **data)
+{
+    SETUP_HOSTDATA(instance);
+    register int c = *count;
+    register unsigned char p = *phase;
+    unsigned long flags;
+
+    /* sanity check */
+    if(!sun3_dma_setup_done) {
+        printk("scsi%d: transfer_dma without setup!\n", HOSTNO);
+        machine_halt();
+    }
+
+    hostdata->dma_len = c;
+
+    DMA_PRINTK("scsi%d: initializing DMA for %s, %d bytes %s %p\n",
+              HOSTNO, (p & SR_IO) ? "reading" : "writing",
+              c, (p & SR_IO) ? "to" : "from", d);
+
+    /* netbsd turns off ints here, why not be safe and do it too */
+    save_flags(flags);
+    cli();
+    
+    /* send start chain */
+    sun3_udc_write(UDC_CHN_START, UDC_CSR);
+    
+    if (p & SR_IO) {
+           NCR5380_write(TARGET_COMMAND_REG, 1);
+           NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+           NCR5380_write(INITIATOR_COMMAND_REG, 0);
+           NCR5380_write(MODE_REG, (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR));
+           NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
+    } else {
+           NCR5380_write(TARGET_COMMAND_REG, 0);
+           NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+           NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_DATA);
+           NCR5380_write(MODE_REG, (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR));
+           NCR5380_write(START_DMA_SEND_REG, 0);
+    }
+
+    restore_flags(flags);
+
+    sun3_dma_active = 1;
+    return 0;
+}
+#endif /* defined(REAL_DMA) */
+
+/*
+ * Function : NCR5380_information_transfer (struct Scsi_Host *instance)
+ *
+ * Purpose : run through the various SCSI phases and do as the target 
+ *     directs us to.  Operates on the currently connected command, 
+ *     instance->connected.
+ *
+ * Inputs : instance, instance for which we are doing commands
+ *
+ * Side effects : SCSI things happen, the disconnected queue will be 
+ *     modified if a command disconnects, *instance->connected will
+ *     change.
+ *
+ * XXX Note : we need to watch for bus free or a reset condition here 
+ *     to recover from an unexpected bus free condition.
+ */
+static void NCR5380_information_transfer (struct Scsi_Host *instance)
+{
+    SETUP_HOSTDATA(instance);
+    unsigned long flags;
+    unsigned char msgout = NOP;
+    int sink = 0;
+    int len;
+#if defined(REAL_DMA)
+    int transfersize;
+#endif
+    unsigned char *data;
+    unsigned char phase, tmp, extended_msg[10], old_phase=0xff;
+    Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
+
+    while (1) {
+       tmp = NCR5380_read(STATUS_REG);
+       /* We only have a valid SCSI phase when REQ is asserted */
+       if (tmp & SR_REQ) {
+           phase = (tmp & PHASE_MASK); 
+           if (phase != old_phase) {
+               old_phase = phase;
+               NCR_PRINT_PHASE(NDEBUG_INFORMATION);
+           }
+
+           if(phase == PHASE_CMDOUT) {
+                   void *d;
+                   unsigned long count;
+
+               if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+                       count = cmd->SCp.buffer->length;
+                       d = cmd->SCp.buffer->address;
+               } else {
+                       count = cmd->SCp.this_residual;
+                       d = cmd->SCp.ptr;
+               }
+#ifdef REAL_DMA
+               /* this command setup for dma yet? */
+               if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done
+                                                 != cmd))
+               {
+                       sun3scsi_dma_setup(d, count,
+                                          cmd->request.cmd);
+                       sun3_dma_setup_done = cmd;
+               }
+#endif
+           }
+
+           
+           if (sink && (phase != PHASE_MSGOUT)) {
+               NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+               NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | 
+                   ICR_ASSERT_ACK);
+               while (NCR5380_read(STATUS_REG) & SR_REQ);
+               NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
+                   ICR_ASSERT_ATN);
+               sink = 0;
+               continue;
+           }
+
+           switch (phase) {
+           case PHASE_DATAOUT:
+#if (NDEBUG & NDEBUG_NO_DATAOUT)
+               printk("scsi%d: NDEBUG_NO_DATAOUT set, attempted DATAOUT "
+                      "aborted\n", HOSTNO);
+               sink = 1;
+               do_abort(instance);
+               cmd->result = DID_ERROR  << 16;
+               cmd->done(cmd);
+               return;
+#endif
+           case PHASE_DATAIN:
+               /* 
+                * If there is no room left in the current buffer in the
+                * scatter-gather list, move onto the next one.
+                */
+               if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+                   ++cmd->SCp.buffer;
+                   --cmd->SCp.buffers_residual;
+                   cmd->SCp.this_residual = cmd->SCp.buffer->length;
+                   cmd->SCp.ptr = cmd->SCp.buffer->address;
+
+                   /* ++roman: Try to merge some scatter-buffers if
+                    * they are at contiguous physical addresses.
+                    */
+//                 merge_contiguous_buffers( cmd );
+                   INF_PRINTK("scsi%d: %d bytes and %d buffers left\n",
+                              HOSTNO, cmd->SCp.this_residual,
+                              cmd->SCp.buffers_residual);
+               }
+
+               /*
+                * The preferred transfer method is going to be 
+                * PSEUDO-DMA for systems that are strictly PIO,
+                * since we can let the hardware do the handshaking.
+                *
+                * For this to work, we need to know the transfersize
+                * ahead of time, since the pseudo-DMA code will sit
+                * in an unconditional loop.
+                */
+
+/* ++roman: I suggest, this should be
+ *   #if def(REAL_DMA)
+ * instead of leaving REAL_DMA out.
+ */
+
+#if defined(REAL_DMA)
+//             if (!cmd->device->borken &&
+               if((transfersize =
+                   NCR5380_dma_xfer_len(instance,cmd,phase)) > SUN3_DMA_MINSIZE) {
+                   len = transfersize;
+                   cmd->SCp.phase = phase;
+
+                   if (NCR5380_transfer_dma(instance, &phase,
+                       &len, (unsigned char **) &cmd->SCp.ptr)) {
+                       /*
+                        * If the watchdog timer fires, all future
+                        * accesses to this device will use the
+                        * polled-IO. */ 
+                       printk(KERN_NOTICE "scsi%d: switching target %d "
+                              "lun %d to slow handshake\n", HOSTNO,
+                              cmd->target, cmd->lun);
+                       cmd->device->borken = 1;
+                       NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
+                           ICR_ASSERT_ATN);
+                       sink = 1;
+                       do_abort(instance);
+                       cmd->result = DID_ERROR  << 16;
+                       cmd->done(cmd);
+                       /* XXX - need to source or sink data here, as appropriate */
+                   } else {
+#ifdef REAL_DMA
+                       /* ++roman: When using real DMA,
+                        * information_transfer() should return after
+                        * starting DMA since it has nothing more to
+                        * do.
+                        */
+                                   return;
+#else                  
+                       cmd->SCp.this_residual -= transfersize - len;
+#endif
+                   }
+               } else 
+#endif /* defined(REAL_DMA) */
+                 NCR5380_transfer_pio(instance, &phase, 
+                   (int *) &cmd->SCp.this_residual, (unsigned char **)
+                   &cmd->SCp.ptr);
+#ifdef REAL_DMA
+               /* if we had intended to dma that command clear it */
+               if(sun3_dma_setup_done == cmd)
+                       sun3_dma_setup_done = NULL;
+#endif
+
+               break;
+           case PHASE_MSGIN:
+               len = 1;
+               data = &tmp;
+               NCR5380_write(SELECT_ENABLE_REG, 0);    /* disable reselects */
+               NCR5380_transfer_pio(instance, &phase, &len, &data);
+               cmd->SCp.Message = tmp;
+
+               switch (tmp) {
+               /*
+                * Linking lets us reduce the time required to get the 
+                * next command out to the device, hopefully this will
+                * mean we don't waste another revolution due to the delays
+                * required by ARBITRATION and another SELECTION.
+                *
+                * In the current implementation proposal, low level drivers
+                * merely have to start the next command, pointed to by 
+                * next_link, done() is called as with unlinked commands.
+                */
+#ifdef LINKED
+               case LINKED_CMD_COMPLETE:
+               case LINKED_FLG_CMD_COMPLETE:
+                   /* Accept message by clearing ACK */
+                   NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+                   
+                   LNK_PRINTK("scsi%d: target %d lun %d linked command "
+                              "complete.\n", HOSTNO, cmd->target, cmd->lun);
+
+                   /* Enable reselect interrupts */
+                   NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+                   /*
+                    * Sanity check : A linked command should only terminate
+                    * with one of these messages if there are more linked
+                    * commands available.
+                    */
+
+                   if (!cmd->next_link) {
+                        printk(KERN_NOTICE "scsi%d: target %d lun %d "
+                               "linked command complete, no next_link\n",
+                               HOSTNO, cmd->target, cmd->lun);
+                           sink = 1;
+                           do_abort (instance);
+                           return;
+                   }
+
+                   initialize_SCp(cmd->next_link);
+                   /* The next command is still part of this process; copy it
+                    * and don't free it! */
+                   cmd->next_link->tag = cmd->tag;
+                   cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); 
+                   LNK_PRINTK("scsi%d: target %d lun %d linked request "
+                              "done, calling scsi_done().\n",
+                              HOSTNO, cmd->target, cmd->lun);
+#ifdef NCR5380_STATS
+                   collect_stats(hostdata, cmd);
+#endif
+                   cmd->scsi_done(cmd);
+                   cmd = hostdata->connected;
+                   break;
+#endif /* def LINKED */
+               case ABORT:
+               case COMMAND_COMPLETE: 
+                   /* Accept message by clearing ACK */
+                   NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+                   hostdata->connected = NULL;
+                   QU_PRINTK("scsi%d: command for target %d, lun %d "
+                             "completed\n", HOSTNO, cmd->target, cmd->lun);
+#ifdef SUPPORT_TAGS
+                   cmd_free_tag( cmd );
+                   if (status_byte(cmd->SCp.Status) == QUEUE_FULL) {
+                       /* Turn a QUEUE FULL status into BUSY, I think the
+                        * mid level cannot handle QUEUE FULL :-( (The
+                        * command is retried after BUSY). Also update our
+                        * queue size to the number of currently issued
+                        * commands now.
+                        */
+                       /* ++Andreas: the mid level code knows about
+                          QUEUE_FULL now. */
+                       TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun];
+                       TAG_PRINTK("scsi%d: target %d lun %d returned "
+                                  "QUEUE_FULL after %d commands\n",
+                                  HOSTNO, cmd->target, cmd->lun,
+                                  ta->nr_allocated);
+                       if (ta->queue_size > ta->nr_allocated)
+                           ta->nr_allocated = ta->queue_size;
+                   }
+#else
+                   hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+#endif
+                   /* Enable reselect interrupts */
+                   NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+
+                   /* 
+                    * I'm not sure what the correct thing to do here is : 
+                    * 
+                    * If the command that just executed is NOT a request 
+                    * sense, the obvious thing to do is to set the result
+                    * code to the values of the stored parameters.
+                    * 
+                    * If it was a REQUEST SENSE command, we need some way to
+                    * differentiate between the failure code of the original
+                    * and the failure code of the REQUEST sense - the obvious
+                    * case is success, where we fall through and leave the
+                    * result code unchanged.
+                    * 
+                    * The non-obvious place is where the REQUEST SENSE failed
+                    */
+
+                   if (cmd->cmnd[0] != REQUEST_SENSE) 
+                       cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); 
+                   else if (status_byte(cmd->SCp.Status) != GOOD)
+                       cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+                   
+#ifdef AUTOSENSE
+                   if ((cmd->cmnd[0] != REQUEST_SENSE) && 
+                       (status_byte(cmd->SCp.Status) == CHECK_CONDITION)) {
+                       ASEN_PRINTK("scsi%d: performing request sense\n",
+                                   HOSTNO);
+                       cmd->cmnd[0] = REQUEST_SENSE;
+                       cmd->cmnd[1] &= 0xe0;
+                       cmd->cmnd[2] = 0;
+                       cmd->cmnd[3] = 0;
+                       cmd->cmnd[4] = sizeof(cmd->sense_buffer);
+                       cmd->cmnd[5] = 0;
+                       cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
+
+                       cmd->use_sg = 0;
+                       /* this is initialized from initialize_SCp 
+                       cmd->SCp.buffer = NULL;
+                       cmd->SCp.buffers_residual = 0;
+                       */
+                       cmd->request_buffer = (char *) cmd->sense_buffer;
+                       cmd->request_bufflen = sizeof(cmd->sense_buffer);
+
+                       save_flags(flags);
+                       cli();
+                       LIST(cmd,hostdata->issue_queue);
+                       NEXT(cmd) = hostdata->issue_queue;
+                       hostdata->issue_queue = (Scsi_Cmnd *) cmd;
+                       restore_flags(flags);
+                       QU_PRINTK("scsi%d: REQUEST SENSE added to head of "
+                                 "issue queue\n", H_NO(cmd));
+                  } else
+#endif /* def AUTOSENSE */
+                  {
+#ifdef NCR5380_STATS
+                      collect_stats(hostdata, cmd);
+#endif
+                      cmd->scsi_done(cmd);
+                   }
+
+                   NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+                   /* 
+                    * Restore phase bits to 0 so an interrupted selection, 
+                    * arbitration can resume.
+                    */
+                   NCR5380_write(TARGET_COMMAND_REG, 0);
+                   
+                   while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+                       barrier();
+
+                   return;
+               case MESSAGE_REJECT:
+                   /* Accept message by clearing ACK */
+                   NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+                   /* Enable reselect interrupts */
+                   NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+                   switch (hostdata->last_message) {
+                   case HEAD_OF_QUEUE_TAG:
+                   case ORDERED_QUEUE_TAG:
+                   case SIMPLE_QUEUE_TAG:
+                       /* The target obviously doesn't support tagged
+                        * queuing, even though it announced this ability in
+                        * its INQUIRY data ?!? (maybe only this LUN?) Ok,
+                        * clear 'tagged_supported' and lock the LUN, since
+                        * the command is treated as untagged further on.
+                        */
+                       cmd->device->tagged_supported = 0;
+                       hostdata->busy[cmd->target] |= (1 << cmd->lun);
+                       cmd->tag = TAG_NONE;
+                       TAG_PRINTK("scsi%d: target %d lun %d rejected "
+                                  "QUEUE_TAG message; tagged queuing "
+                                  "disabled\n",
+                                  HOSTNO, cmd->target, cmd->lun);
+                       break;
+                   }
+                   break;
+               case DISCONNECT:
+                   /* Accept message by clearing ACK */
+                   NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+                   save_flags(flags);
+                   cli();
+                   cmd->device->disconnect = 1;
+                   LIST(cmd,hostdata->disconnected_queue);
+                   NEXT(cmd) = hostdata->disconnected_queue;
+                   hostdata->connected = NULL;
+                   hostdata->disconnected_queue = cmd;
+                   restore_flags(flags);
+                   QU_PRINTK("scsi%d: command for target %d lun %d was "
+                             "moved from connected to the "
+                             "disconnected_queue\n", HOSTNO, 
+                             cmd->target, cmd->lun);
+                   /* 
+                    * Restore phase bits to 0 so an interrupted selection, 
+                    * arbitration can resume.
+                    */
+                   NCR5380_write(TARGET_COMMAND_REG, 0);
+
+                   /* Enable reselect interrupts */
+                   NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+                   /* Wait for bus free to avoid nasty timeouts */
+                   while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+                       barrier();
+                   return;
+               /* 
+                * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
+                * operation, in violation of the SCSI spec so we can safely 
+                * ignore SAVE/RESTORE pointers calls.
+                *
+                * Unfortunately, some disks violate the SCSI spec and 
+                * don't issue the required SAVE_POINTERS message before
+                * disconnecting, and we have to break spec to remain 
+                * compatible.
+                */
+               case SAVE_POINTERS:
+               case RESTORE_POINTERS:
+                   /* Accept message by clearing ACK */
+                   NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+                   /* Enable reselect interrupts */
+                   NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+                   break;
+               case EXTENDED_MESSAGE:
+/* 
+ * Extended messages are sent in the following format :
+ * Byte        
+ * 0           EXTENDED_MESSAGE == 1
+ * 1           length (includes one byte for code, doesn't 
+ *             include first two bytes)
+ * 2           code
+ * 3..length+1 arguments
+ *
+ * Start the extended message buffer with the EXTENDED_MESSAGE
+ * byte, since print_msg() wants the whole thing.  
+ */
+                   extended_msg[0] = EXTENDED_MESSAGE;
+                   /* Accept first byte by clearing ACK */
+                   NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+                   EXT_PRINTK("scsi%d: receiving extended message\n", HOSTNO);
+
+                   len = 2;
+                   data = extended_msg + 1;
+                   phase = PHASE_MSGIN;
+                   NCR5380_transfer_pio(instance, &phase, &len, &data);
+                   EXT_PRINTK("scsi%d: length=%d, code=0x%02x\n", HOSTNO,
+                              (int)extended_msg[1], (int)extended_msg[2]);
+
+                   if (!len && extended_msg[1] <= 
+                       (sizeof (extended_msg) - 1)) {
+                       /* Accept third byte by clearing ACK */
+                       NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+                       len = extended_msg[1] - 1;
+                       data = extended_msg + 3;
+                       phase = PHASE_MSGIN;
+
+                       NCR5380_transfer_pio(instance, &phase, &len, &data);
+                       EXT_PRINTK("scsi%d: message received, residual %d\n",
+                                  HOSTNO, len);
+
+                       switch (extended_msg[2]) {
+                       case EXTENDED_SDTR:
+                       case EXTENDED_WDTR:
+                       case EXTENDED_MODIFY_DATA_POINTER:
+                       case EXTENDED_EXTENDED_IDENTIFY:
+                           tmp = 0;
+                       }
+                   } else if (len) {
+                       printk(KERN_NOTICE "scsi%d: error receiving "
+                              "extended message\n", HOSTNO);
+                       tmp = 0;
+                   } else {
+                       printk(KERN_NOTICE "scsi%d: extended message "
+                              "code %02x length %d is too long\n",
+                              HOSTNO, extended_msg[2], extended_msg[1]);
+                       tmp = 0;
+                   }
+               /* Fall through to reject message */
+
+               /* 
+                * If we get something weird that we aren't expecting, 
+                * reject it.
+                */
+               default:
+                   if (!tmp) {
+                       printk(KERN_DEBUG "scsi%d: rejecting message ", HOSTNO);
+                       print_msg (extended_msg);
+                       printk("\n");
+                   } else if (tmp != EXTENDED_MESSAGE)
+                       printk(KERN_DEBUG "scsi%d: rejecting unknown "
+                              "message %02x from target %d, lun %d\n",
+                              HOSTNO, tmp, cmd->target, cmd->lun);
+                   else
+                       printk(KERN_DEBUG "scsi%d: rejecting unknown "
+                              "extended message "
+                              "code %02x, length %d from target %d, lun %d\n",
+                              HOSTNO, extended_msg[1], extended_msg[0],
+                              cmd->target, cmd->lun);
+   
+
+                   msgout = MESSAGE_REJECT;
+                   NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
+                       ICR_ASSERT_ATN);
+                   break;
+               } /* switch (tmp) */
+               break;
+           case PHASE_MSGOUT:
+               len = 1;
+               data = &msgout;
+               hostdata->last_message = msgout;
+               NCR5380_transfer_pio(instance, &phase, &len, &data);
+               if (msgout == ABORT) {
+#ifdef SUPPORT_TAGS
+                   cmd_free_tag( cmd );
+#else
+                   hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+#endif
+                   hostdata->connected = NULL;
+                   cmd->result = DID_ERROR << 16;
+#ifdef NCR5380_STATS
+                   collect_stats(hostdata, cmd);
+#endif
+                   cmd->scsi_done(cmd);
+                   NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+                   return;
+               }
+               msgout = NOP;
+               break;
+           case PHASE_CMDOUT:
+               len = cmd->cmd_len;
+               data = cmd->cmnd;
+               /* 
+                * XXX for performance reasons, on machines with a 
+                * PSEUDO-DMA architecture we should probably 
+                * use the dma transfer function.  
+                */
+               NCR5380_transfer_pio(instance, &phase, &len, 
+                   &data);
+               break;
+           case PHASE_STATIN:
+               len = 1;
+               data = &tmp;
+               NCR5380_transfer_pio(instance, &phase, &len, &data);
+               cmd->SCp.Status = tmp;
+               break;
+           default:
+               printk("scsi%d: unknown phase\n", HOSTNO);
+               NCR_PRINT(NDEBUG_ANY);
+           } /* switch(phase) */
+       } /* if (tmp * SR_REQ) */ 
+    } /* while (1) */
+}
+
+/*
+ * Function : void NCR5380_reselect (struct Scsi_Host *instance)
+ *
+ * Purpose : does reselection, initializing the instance->connected 
+ *     field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q 
+ *     nexus has been reestablished,
+ *     
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ */
+
+/* it might eventually prove necessary to do a dma setup on
+   reselection, but it doesn't seem to be needed now -- sam */
+
+static void NCR5380_reselect (struct Scsi_Host *instance)
+{
+    SETUP_HOSTDATA(instance);
+    unsigned char target_mask;
+    unsigned char lun;
+#ifdef SUPPORT_TAGS
+    unsigned char tag;
+#endif
+    unsigned char msg[3];
+    Scsi_Cmnd *tmp = NULL, *prev;
+/*    unsigned long flags; */
+
+    /*
+     * Disable arbitration, etc. since the host adapter obviously
+     * lost, and tell an interrupted NCR5380_select() to restart.
+     */
+
+    NCR5380_write(MODE_REG, MR_BASE);
+    hostdata->restart_select = 1;
+
+    target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask);
+
+    RSL_PRINTK("scsi%d: reselect\n", HOSTNO);
+
+    /* 
+     * At this point, we have detected that our SCSI ID is on the bus,
+     * SEL is true and BSY was false for at least one bus settle delay
+     * (400 ns).
+     *
+     * We must assert BSY ourselves, until the target drops the SEL
+     * signal.
+     */
+
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY);
+    
+    while (NCR5380_read(STATUS_REG) & SR_SEL);
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+    /*
+     * Wait for target to go into MSGIN.
+     */
+
+    while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+
+#if 1
+    // acknowledge toggle to MSGIN
+    NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(PHASE_MSGIN));
+
+    // peek at the byte without really hitting the bus
+    msg[0] = NCR5380_read(CURRENT_SCSI_DATA_REG);
+#endif
+
+    if (!msg[0] & 0x80) {
+       printk(KERN_DEBUG "scsi%d: expecting IDENTIFY message, got ", HOSTNO);
+       print_msg(msg);
+       do_abort(instance);
+       return;
+    }
+    lun = (msg[0] & 0x07);
+
+    /* 
+     * Find the command corresponding to the I_T_L or I_T_L_Q  nexus we 
+     * just reestablished, and remove it from the disconnected queue.
+     */
+
+    for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL; 
+        tmp; prev = tmp, tmp = NEXT(tmp) ) {
+       if ((target_mask == (1 << tmp->target)) && (lun == tmp->lun)
+#ifdef SUPPORT_TAGS
+           && (tag == tmp->tag) 
+#endif
+           ) {
+           if (prev) {
+               REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
+               NEXT(prev) = NEXT(tmp);
+           } else {
+               REMOVE(-1, hostdata->disconnected_queue, tmp, NEXT(tmp));
+               hostdata->disconnected_queue = NEXT(tmp);
+           }
+           NEXT(tmp) = NULL;
+           break;
+       }
+    }
+    
+    if (!tmp) {
+       printk(KERN_WARNING "scsi%d: warning: target bitmask %02x lun %d "
+#ifdef SUPPORT_TAGS
+               "tag %d "
+#endif
+               "not in disconnected_queue.\n",
+               HOSTNO, target_mask, lun
+#ifdef SUPPORT_TAGS
+               , tag
+#endif
+               );
+       /* 
+        * Since we have an established nexus that we can't do anything
+        * with, we must abort it.  
+        */
+       do_abort(instance);
+       return;
+    }
+#if 1
+    /* engage dma setup for the command we just saw */
+    {
+           void *d;
+                   unsigned long count;
+
+               if (!tmp->SCp.this_residual && tmp->SCp.buffers_residual) {
+                       count = tmp->SCp.buffer->length;
+                       d = tmp->SCp.buffer->address;
+               } else {
+                       count = tmp->SCp.this_residual;
+                       d = tmp->SCp.ptr;
+               }
+#ifdef REAL_DMA                
+               /* setup this command for dma if not already */
+               if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done
+                                                 != tmp))
+               {
+                       sun3scsi_dma_setup(d, count,
+                                          tmp->request.cmd);
+                       sun3_dma_setup_done = tmp;
+               }
+#endif
+    }
+#endif
+
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
+    /* Accept message by clearing ACK */
+    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+#ifdef SUPPORT_TAGS
+    /* If the phase is still MSGIN, the target wants to send some more
+     * messages. In case it supports tagged queuing, this is probably a
+     * SIMPLE_QUEUE_TAG for the I_T_L_Q nexus.
+     */
+    tag = TAG_NONE;
+    if (phase == PHASE_MSGIN && setup_use_tagged_queuing) {
+       /* Accept previous IDENTIFY message by clearing ACK */
+       NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
+       len = 2;
+       data = msg+1;
+       if (!NCR5380_transfer_pio(instance, &phase, &len, &data) &&
+           msg[1] == SIMPLE_QUEUE_TAG)
+           tag = msg[2];
+       TAG_PRINTK("scsi%d: target mask %02x, lun %d sent tag %d at "
+                  "reselection\n", HOSTNO, target_mask, lun, tag);
+    }
+#endif
+    
+    hostdata->connected = tmp;
+    RSL_PRINTK("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n",
+              HOSTNO, tmp->target, tmp->lun, tmp->tag);
+}
+
+
+/*
+ * Function : int NCR5380_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : abort a command
+ *
+ * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the 
+ *     host byte of the result field to, if zero DID_ABORTED is 
+ *     used.
+ *
+ * Returns : 0 - success, -1 on failure.
+ *
+ * XXX - there is no way to abort the command that is currently 
+ *      connected, you have to wait for it to complete.  If this is 
+ *      a problem, we could implement longjmp() / setjmp(), setjmp()
+ *      called where the loop started in NCR5380_main().
+ */
+
+#ifndef NCR5380_abort
+static
+#endif
+int NCR5380_abort (Scsi_Cmnd *cmd)
+{
+    struct Scsi_Host *instance = cmd->host;
+    SETUP_HOSTDATA(instance);
+    Scsi_Cmnd *tmp, **prev;
+    unsigned long flags;
+
+    printk(KERN_NOTICE "scsi%d: aborting command\n", HOSTNO);
+    print_Scsi_Cmnd (cmd);
+
+    NCR5380_print_status (instance);
+
+    save_flags(flags);
+    cli();
+    
+    ABRT_PRINTK("scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO,
+               NCR5380_read(BUS_AND_STATUS_REG),
+               NCR5380_read(STATUS_REG));
+
+#if 1
+/* 
+ * Case 1 : If the command is the currently executing command, 
+ * we'll set the aborted flag and return control so that 
+ * information transfer routine can exit cleanly.
+ */
+
+    if (hostdata->connected == cmd) {
+
+       ABRT_PRINTK("scsi%d: aborting connected command\n", HOSTNO);
+/*
+ * We should perform BSY checking, and make sure we haven't slipped
+ * into BUS FREE.
+ */
+
+/*     NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN); */
+/* 
+ * Since we can't change phases until we've completed the current 
+ * handshake, we have to source or sink a byte of data if the current
+ * phase is not MSGOUT.
+ */
+
+/* 
+ * Return control to the executing NCR drive so we can clear the
+ * aborted flag and get back into our main loop.
+ */ 
+
+       if (do_abort(instance) == 0) {
+         hostdata->aborted = 1;
+         hostdata->connected = NULL;
+         cmd->result = DID_ABORT << 16;
+#ifdef SUPPORT_TAGS
+         cmd_free_tag( cmd );
+#else
+         hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+#endif
+         restore_flags(flags);
+         cmd->scsi_done(cmd);
+         return SCSI_ABORT_SUCCESS;
+       } else {
+/*       restore_flags(flags); */
+         printk("scsi%d: abort of connected command failed!\n", HOSTNO);
+         return SCSI_ABORT_ERROR;
+       } 
+   }
+#endif
+
+/* 
+ * Case 2 : If the command hasn't been issued yet, we simply remove it 
+ *         from the issue queue.
+ */
+    for (prev = (Scsi_Cmnd **) &(hostdata->issue_queue), 
+       tmp = (Scsi_Cmnd *) hostdata->issue_queue;
+       tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) )
+       if (cmd == tmp) {
+           REMOVE(5, *prev, tmp, NEXT(tmp));
+           (*prev) = NEXT(tmp);
+           NEXT(tmp) = NULL;
+           tmp->result = DID_ABORT << 16;
+           restore_flags(flags);
+           ABRT_PRINTK("scsi%d: abort removed command from issue queue.\n",
+                       HOSTNO);
+           /* Tagged queuing note: no tag to free here, hasn't been assigned
+            * yet... */
+           tmp->scsi_done(tmp);
+           return SCSI_ABORT_SUCCESS;
+       }
+
+/* 
+ * Case 3 : If any commands are connected, we're going to fail the abort
+ *         and let the high level SCSI driver retry at a later time or 
+ *         issue a reset.
+ *
+ *         Timeouts, and therefore aborted commands, will be highly unlikely
+ *          and handling them cleanly in this situation would make the common
+ *         case of noresets less efficient, and would pollute our code.  So,
+ *         we fail.
+ */
+
+    if (hostdata->connected) {
+       restore_flags(flags);
+       ABRT_PRINTK("scsi%d: abort failed, command connected.\n", HOSTNO);
+        return SCSI_ABORT_SNOOZE;
+    }
+
+/*
+ * Case 4: If the command is currently disconnected from the bus, and 
+ *     there are no connected commands, we reconnect the I_T_L or 
+ *     I_T_L_Q nexus associated with it, go into message out, and send 
+ *      an abort message.
+ *
+ * This case is especially ugly. In order to reestablish the nexus, we
+ * need to call NCR5380_select().  The easiest way to implement this 
+ * function was to abort if the bus was busy, and let the interrupt
+ * handler triggered on the SEL for reselect take care of lost arbitrations
+ * where necessary, meaning interrupts need to be enabled.
+ *
+ * When interrupts are enabled, the queues may change - so we 
+ * can't remove it from the disconnected queue before selecting it
+ * because that could cause a failure in hashing the nexus if that 
+ * device reselected.
+ * 
+ * Since the queues may change, we can't use the pointers from when we
+ * first locate it.
+ *
+ * So, we must first locate the command, and if NCR5380_select()
+ * succeeds, then issue the abort, relocate the command and remove
+ * it from the disconnected queue.
+ */
+
+    for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp;
+        tmp = NEXT(tmp)) 
+        if (cmd == tmp) {
+            restore_flags(flags);
+           ABRT_PRINTK("scsi%d: aborting disconnected command.\n", HOSTNO);
+  
+            if (NCR5380_select (instance, cmd, (int) cmd->tag)) 
+               return SCSI_ABORT_BUSY;
+
+           ABRT_PRINTK("scsi%d: nexus reestablished.\n", HOSTNO);
+
+           do_abort (instance);
+
+           save_flags(flags);
+           cli();
+           for (prev = (Scsi_Cmnd **) &(hostdata->disconnected_queue), 
+               tmp = (Scsi_Cmnd *) hostdata->disconnected_queue;
+               tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) )
+                   if (cmd == tmp) {
+                   REMOVE(5, *prev, tmp, NEXT(tmp));
+                   *prev = NEXT(tmp);
+                   NEXT(tmp) = NULL;
+                   tmp->result = DID_ABORT << 16;
+                   /* We must unlock the tag/LUN immediately here, since the
+                    * target goes to BUS FREE and doesn't send us another
+                    * message (COMMAND_COMPLETE or the like)
+                    */
+#ifdef SUPPORT_TAGS
+                   cmd_free_tag( tmp );
+#else
+                   hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+#endif
+                   restore_flags(flags);
+                   tmp->scsi_done(tmp);
+                   return SCSI_ABORT_SUCCESS;
+               }
+       }
+
+/*
+ * Case 5 : If we reached this point, the command was not found in any of 
+ *         the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke.
+ */
+
+    restore_flags(flags);
+    printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully\n"
+           KERN_INFO "        before abortion\n", HOSTNO); 
+
+    return SCSI_ABORT_NOT_RUNNING;
+}
+
+
+/* 
+ * Function : int NCR5380_reset (Scsi_Cmnd *cmd, unsigned int reset_flags)
+ * 
+ * Purpose : reset the SCSI bus.
+ *
+ * Returns : SCSI_RESET_WAKEUP
+ *
+ */ 
+
+int NCR5380_reset( Scsi_Cmnd *cmd, unsigned int reset_flags)
+{
+    SETUP_HOSTDATA(cmd->host);
+    int           i;
+    unsigned long flags;
+#if 1
+    Scsi_Cmnd *connected, *disconnected_queue;
+#endif
+
+
+    NCR5380_print_status (cmd->host);
+
+    /* get in phase */
+    NCR5380_write( TARGET_COMMAND_REG,
+                  PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) ));
+    /* assert RST */
+    NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST );
+    udelay (40);
+    /* reset NCR registers */
+    NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
+    NCR5380_write( MODE_REG, MR_BASE );
+    NCR5380_write( TARGET_COMMAND_REG, 0 );
+    NCR5380_write( SELECT_ENABLE_REG, 0 );
+    /* ++roman: reset interrupt condition! otherwise no interrupts don't get
+     * through anymore ... */
+    (void)NCR5380_read( RESET_PARITY_INTERRUPT_REG );
+
+#if 1 /* XXX Should now be done by midlevel code, but it's broken XXX */
+      /* XXX see below                                            XXX */
+
+    /* MSch: old-style reset: actually abort all command processing here */
+
+    /* After the reset, there are no more connected or disconnected commands
+     * and no busy units; to avoid problems with re-inserting the commands
+     * into the issue_queue (via scsi_done()), the aborted commands are
+     * remembered in local variables first.
+     */
+    save_flags(flags);
+    cli();
+    connected = (Scsi_Cmnd *)hostdata->connected;
+    hostdata->connected = NULL;
+    disconnected_queue = (Scsi_Cmnd *)hostdata->disconnected_queue;
+    hostdata->disconnected_queue = NULL;
+#ifdef SUPPORT_TAGS
+    free_all_tags();
+#endif
+    for( i = 0; i < 8; ++i )
+       hostdata->busy[i] = 0;
+#ifdef REAL_DMA
+    hostdata->dma_len = 0;
+#endif
+    restore_flags(flags);
+
+    /* In order to tell the mid-level code which commands were aborted, 
+     * set the command status to DID_RESET and call scsi_done() !!!
+     * This ultimately aborts processing of these commands in the mid-level.
+     */
+
+    if ((cmd = connected)) {
+       ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd));
+       cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
+       cmd->scsi_done( cmd );
+    }
+
+    for (i = 0; (cmd = disconnected_queue); ++i) {
+       disconnected_queue = NEXT(cmd);
+       NEXT(cmd) = NULL;
+       cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
+       cmd->scsi_done( cmd );
+    }
+    if (i > 0)
+       ABRT_PRINTK("scsi: reset aborted %d disconnected command(s)\n", i);
+
+
+    /* since all commands have been explicitly terminated, we need to tell
+     * the midlevel code that the reset was SUCCESSFUL, and there is no 
+     * need to 'wake up' the commands by a request_sense
+     */
+    return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
+#else /* 1 */
+
+    /* MSch: new-style reset handling: let the mid-level do what it can */
+
+    /* ++guenther: MID-LEVEL IS STILL BROKEN.
+     * Mid-level is supposed to requeue all commands that were active on the
+     * various low-level queues. In fact it does this, but that's not enough
+     * because all these commands are subject to timeout. And if a timeout
+     * happens for any removed command, *_abort() is called but all queues
+     * are now empty. Abort then gives up the falcon lock, which is fatal,
+     * since the mid-level will queue more commands and must have the lock
+     * (it's all happening inside timer interrupt handler!!).
+     * Even worse, abort will return NOT_RUNNING for all those commands not
+     * on any queue, so they won't be retried ...
+     *
+     * Conclusion: either scsi.c disables timeout for all resetted commands
+     * immediately, or we loose!  As of linux-2.0.20 it doesn't.
+     */
+
+    /* After the reset, there are no more connected or disconnected commands
+     * and no busy units; so clear the low-level status here to avoid 
+     * conflicts when the mid-level code tries to wake up the affected 
+     * commands!
+     */
+
+    if (hostdata->issue_queue)
+       ABRT_PRINTK("scsi%d: reset aborted issued command(s)\n", H_NO(cmd));
+    if (hostdata->connected) 
+       ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd));
+    if (hostdata->disconnected_queue)
+       ABRT_PRINTK("scsi%d: reset aborted disconnected command(s)\n", H_NO(cmd));
+
+    save_flags(flags);
+    cli();
+    hostdata->issue_queue = NULL;
+    hostdata->connected = NULL;
+    hostdata->disconnected_queue = NULL;
+#ifdef SUPPORT_TAGS
+    free_all_tags();
+#endif
+    for( i = 0; i < 8; ++i )
+       hostdata->busy[i] = 0;
+#ifdef REAL_DMA
+    hostdata->dma_len = 0;
+#endif
+    restore_flags(flags);
+
+    /* we did no complete reset of all commands, so a wakeup is required */
+    return SCSI_RESET_WAKEUP | SCSI_RESET_BUS_RESET;
+#endif /* 1 */
+}
+
+/* Local Variables: */
+/* tab-width: 8     */
+/* End:             */
index ba0be94dd36ab21451a65309de8abefd1f6c05f8..45f504fa3967d6a513c8c3d973a69b77ead76147 100644 (file)
@@ -1,6 +1,8 @@
 /*
  * Sun3 SCSI stuff by Erik Verbruggen (erik@bigmama.xtdnet.nl)
  *
+ * Sun3 DMA routines added by Sam Creasey (sammy@oh.verio.com)
+ *
  * Adapted from mac_scsinew.c:
  */
 /*
  */
 
 /*
- * $Log: mac_NCR5380.c,v $
+ * $Log: sun3_NCR5380.c,v $
  */
 
 #define AUTOSENSE
-#if 0
-#define PSEUDO_DMA
-#endif
 
 #include <linux/types.h>
 #include <linux/stddef.h>
@@ -68,6 +67,9 @@
 #include <asm/system.h>
 
 #include <asm/sun3ints.h>
+#include <asm/dvma.h>
+/* dma on! */
+#define REAL_DMA
 
 #include "scsi.h"
 #include "hosts.h"
 #include "NCR5380.h"
 #include "constants.h"
 
-#if 0
-#define NDEBUG (NDEBUG_INTR | NDEBUG_PSEUDO_DMA | NDEBUG_ARBITRATION | NDEBUG_SELECTION | NDEBUG_RESELECTION)
-#define NCR_TIMEOUT 100
-#else
-#define NDEBUG (NDEBUG_ABORT)
-#endif
-
 #define USE_WRAPPER
 #define RESET_BOOT
 #define DRIVER_SETUP
 
+#define NDEBUG 0
+
 /*
  * BUG can be used to trigger a strange code-size related hang on 2.1 kernels
  */
 #undef DRIVER_SETUP
 #endif
 
-#define        ENABLE_IRQ()    sun3_enable_irq( IRQ_SUN3_SCSI ); 
-#define        DISABLE_IRQ()   sun3_enable_irq( IRQ_SUN3_SCSI );
+#undef SUPPORT_TAGS
+
+#define        ENABLE_IRQ()    enable_irq( IRQ_SUN3_SCSI ); 
 
-/* extern void via_scsi_clear(void); */
 
 static void scsi_sun3_intr(int irq, void *dummy, struct pt_regs *fp);
-static char sun3scsi_read(struct Scsi_Host *instance, int reg);
-static void sun3scsi_write(struct Scsi_Host *instance, int reg, int value);
+static inline unsigned char sun3scsi_read(int reg);
+static inline void sun3scsi_write(int reg, int value);
 
 static int setup_can_queue = -1;
 static int setup_cmd_per_lun = -1;
@@ -111,38 +108,65 @@ static int setup_use_tagged_queuing = -1;
 #endif
 static int setup_hostid = -1;
 
-static int polled_scsi_on = 0;
+static Scsi_Cmnd *sun3_dma_setup_done = NULL;
 
 #define        AFTER_RESET_DELAY       (HZ/2)
 
-static volatile unsigned char *sun3_scsi_regp = IOBASE_SUN3_SCSI;
-/*
-static volatile unsigned char *sun3_scsi_drq  = NULL;
-static volatile unsigned char *sun3_scsi_nodrq = NULL;
-*/
+/* ms to wait after hitting dma regs */
+#define SUN3_DMA_DELAY 5
+
+/* dvma buffer to allocate -- 32k should hopefully be more than sufficient */
+#define SUN3_DVMA_BUFSIZE 0xe000
+
+/* minimum number of bytes to to dma on */
+#define SUN3_DMA_MINSIZE 128
+
+static struct proc_dir_entry proc_scsi_sun3_5380 = {
+       PROC_SCSI_MAC, 13, "Sun3 5380 SCSI", S_IFDIR | S_IRUGO, S_IXUGO, 2
+};
+
+static volatile unsigned char *sun3_scsi_regp;
+static volatile struct sun3_dma_regs *dregs;
+static unsigned char *dmabuf = NULL; /* dma memory buffer */
+static struct sun3_udc_regs *udc_regs = NULL;
+static void *sun3_dma_orig_addr = NULL;
+static unsigned long sun3_dma_orig_count = 0;
+static int sun3_dma_active = 0;
 
 /*
- * Function : sun3_scsi_setup(char *str, int *ints)
- *
- * Purpose : booter command line initialization of the overrides array,
- *
- * Inputs : str - unused, ints - array of integer parameters with ints[0]
- *     equal to the number of ints.
- *
- * TODO: make it actually work!
- *
+ * NCR 5380 register access functions
  */
 
-void sun3_scsi_setup(char *str, int *ints) {
-       printk("sun3_scsi_setup() called\n");
-       setup_can_queue = -1;
-       setup_cmd_per_lun = -1;
-       setup_sg_tablesize = -1;
-       setup_hostid = -1;
-#ifdef SUPPORT_TAGS
-       setup_use_tagged_queuing = -1;
-#endif
-       printk("sun3_scsi_setup() done\n");
+static inline unsigned char sun3scsi_read(int reg)
+{
+       return( sun3_scsi_regp[reg] );
+}
+
+static inline void sun3scsi_write(int reg, int value)
+{
+       sun3_scsi_regp[reg] = value;
+}
+
+/* dma controller register access functions */
+
+static inline unsigned short sun3_udc_read(unsigned char reg)
+{
+       unsigned short ret;
+
+       dregs->udc_addr = UDC_CSR;
+       udelay(SUN3_DMA_DELAY);
+       ret = dregs->udc_data;
+       udelay(SUN3_DMA_DELAY);
+       
+       return ret;
+}
+
+static inline void sun3_udc_write(unsigned short val, unsigned char reg)
+{
+       dregs->udc_addr = reg;
+       udelay(SUN3_DMA_DELAY);
+       dregs->udc_data = val;
+       udelay(SUN3_DMA_DELAY);
 }
 
 /*
@@ -165,7 +189,6 @@ static struct Scsi_Host *default_instance;
 int sun3scsi_detect(Scsi_Host_Template * tpnt)
 {
        unsigned long ioaddr, iopte;
-       unsigned short *ioptr;
        int count = 0;
        static int called = 0;
        struct Scsi_Host *instance;
@@ -173,9 +196,7 @@ int sun3scsi_detect(Scsi_Host_Template * tpnt)
        if(called)
                return 0;
 
-printk("sun3scsi_detect(0x%p)\n",tpnt);
-
-       tpnt->proc_name = "Sun3 5380 SCSI"; /* Could you spell "ewww..."? */
+       tpnt->proc_dir = &proc_scsi_sun3_5380;
 
        /* setup variables */
        tpnt->can_queue =
@@ -204,7 +225,6 @@ printk("sun3scsi_detect(0x%p)\n",tpnt);
                 if(((iopte & SUN3_PAGE_PGNUM_MASK) << PAGE_SHIFT) ==
                    IOBASE_SUN3_SCSI) {
                         count = 1;
-printk("Found ioaddr in pmeg\n");
                         break;
                 }
         }
@@ -214,22 +234,19 @@ printk("Found ioaddr in pmeg\n");
                return 0;
        }
 
-       sun3_scsi_regp = ioaddr;
-
-       /* doing some stuff like resetting DVMA: */
-       ioptr = ioaddr;
-        *(ioptr+8) = 0;
-       udelay(10);
-        *(ioptr+9) = 0;
-       udelay(10);
-        *(ioptr+12) = 0;
-       udelay(10);
-        *(ioptr+12) = 0x7;
-       udelay(10);
-       printk("SCSI status reg = %x\n", *(ioptr+12));
-       udelay(10);
-        *(ioptr+13) = 0;
-       udelay(10);
+       sun3_scsi_regp = (unsigned char *)ioaddr;
+       dregs = (struct sun3_dma_regs *)(((unsigned char *)ioaddr) + 8);
+
+       if((dmabuf = sun3_dvma_malloc(SUN3_DVMA_BUFSIZE)) == NULL) {
+            printk("SUN3 Scsi couldn't allocate DVMA memory!\n");
+            return 0;
+       }
+
+       if((udc_regs = sun3_dvma_malloc(sizeof(struct sun3_udc_regs)))
+          == NULL) {
+            printk("SUN3 Scsi couldn't allocate DVMA memory!\n");
+            return 0;
+       }
 
 #ifdef SUPPORT_TAGS
        if (setup_use_tagged_queuing < 0)
@@ -239,18 +256,6 @@ printk("Found ioaddr in pmeg\n");
        instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
        default_instance = instance;
 
-/*
-       if (macintosh_config->ident == MAC_MODEL_IIFX) {
-               mac_scsi_regp  = via1_regp+0x8000;
-               mac_scsi_drq   = via1_regp+0x6000;
-               mac_scsi_nodrq = via1_regp+0x12000;
-       } else {
-               mac_scsi_regp  = via1_regp+0x10000;
-               mac_scsi_drq   = via1_regp+0x6000;
-               mac_scsi_nodrq = via1_regp+0x12000;
-       }
-*/
-
         instance->io_port = (unsigned long) ioaddr;
        instance->irq = IRQ_SUN3_SCSI;
 
@@ -260,15 +265,20 @@ printk("Found ioaddr in pmeg\n");
 
         ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0;
 
-       if (instance->irq != IRQ_NONE)
-               if (sun3_request_irq(instance->irq, sun3scsi_intr,
-                               0, "Sun3SCSI-5380", NULL)) {
-                       printk("scsi%d: IRQ%d not free, interrupts disabled\n",
-                              instance->host_no, instance->irq);
-                       instance->irq = IRQ_NONE;
-               }
-
-       printk("scsi%d: generic 5380 at port %lX irq", instance->host_no, instance->io_port);
+       if (request_irq(instance->irq, scsi_sun3_intr,
+                            0, "Sun3SCSI-5380", NULL)) {
+#ifndef REAL_DMA
+               printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+                      instance->host_no, instance->irq);
+               instance->irq = IRQ_NONE;
+#else
+               printk("scsi%d: IRQ%d not free, bailing out\n",
+                      instance->host_no, instance->irq);
+               return 0;
+#endif
+       }
+       
+       printk("scsi%d: Sun3 5380 at port %lX irq", instance->host_no, instance->io_port);
        if (instance->irq == IRQ_NONE)
                printk ("s disabled");
        else
@@ -280,6 +290,12 @@ printk("Found ioaddr in pmeg\n");
        NCR5380_print_options(instance);
        printk("\n");
 
+       dregs->csr = 0;
+       udelay(SUN3_DMA_DELAY);
+       dregs->csr = CSR_SCSI | CSR_FIFO | CSR_INTR;
+       udelay(SUN3_DMA_DELAY);
+       dregs->fifo_count = 0;
+
        called = 1;
        return 1;
 }
@@ -342,34 +358,34 @@ const char * sun3scsi_info (struct Scsi_Host *spnt) {
     return "";
 }
 
+// safe bits for the CSR
+#define CSR_GOOD 0x060f
 
-/*
- * NCR 5380 register access functions
- */
-
-static char sun3scsi_read(struct Scsi_Host *instance, int reg)
+static void scsi_sun3_intr(int irq, void *dummy, struct pt_regs *fp)
 {
-/*
-printk("sun3scsi_read(instance=0x%p, reg=0x%x): @0x%p= %d\n",instance,reg,sun3_scsi_regp,sun3_scsi_regp[reg]);
-*/
-       return( sun3_scsi_regp[reg] );
-}
+       unsigned short csr = dregs->csr;
 
-static void sun3scsi_write(struct Scsi_Host *instance, int reg, int value)
-{
-/*
-       printk("sun3scsi_write(instance=0x%p, reg=0x%x, value=0x%x)\n", instance, reg, value);
-*/
-       sun3_scsi_regp[reg] = value;
-}
+       if(csr & ~CSR_GOOD) {
+               if(csr & CSR_DMA_BUSERR) {
+                       printk("scsi%d: bus error in dma\n", default_instance->host_no);
+               }
 
-#include "NCR5380.c"
+               if(csr & CSR_DMA_CONFLICT) {
+                       printk("scsi%d: dma conflict\n", default_instance->host_no);
+               }
+       }
+
+       if(csr & (CSR_SDB_INT | CSR_DMA_INT))
+               NCR5380_intr(irq, dummy, fp);
+}
 
 /*
  * Debug stuff - to be called on NMI, or sysrq key. Use at your own risk; 
  * reentering NCR5380_print_status seems to have ugly side effects
  */
 
+/* this doesn't seem to get used at all -- sam */
+#if 0
 void sun3_sun3_debug (void)
 {
        unsigned long flags;
@@ -381,32 +397,141 @@ void sun3_sun3_debug (void)
                        NCR5380_print_status(default_instance);
                        restore_flags(flags);
        }
-#if 0
-       polled_scsi_on = 1;
-#endif
 }
-/*
- * Helper function for interrupt trouble. More ugly side effects here.
- */
+#endif
+
 
-void scsi_sun3_polled (void)
+/* sun3scsi_dma_setup() -- initialize the dma controller for a read/write */
+static unsigned long sun3scsi_dma_setup(void *data, unsigned long count, int write_flag)
 {
-       unsigned long flags;
-       NCR5380_local_declare();
-       struct Scsi_Host *instance;
+       if(write_flag) 
+               memcpy(dmabuf, data, count);
+       else {
+               sun3_dma_orig_addr = data;
+               sun3_dma_orig_count = count;
+       }
+
+       dregs->fifo_count = 0;
+       sun3_udc_write(UDC_RESET, UDC_CSR);
        
-       instance = default_instance;
-       NCR5380_setup(instance);
-       if(NCR5380_read(BUS_AND_STATUS_REG)&BASR_IRQ)
-       {
-               printk("SCSI poll\n");
-               save_flags(flags);
-               cli();
-               sun3scsi_intr(IRQ_SUN3_SCSI, instance, NULL);
-               restore_flags(flags);
+       /* reset fifo */
+       dregs->csr &= ~CSR_FIFO;
+       dregs->csr |= CSR_FIFO;
+       
+       /* set direction */
+       if(write_flag)
+               dregs->csr |= CSR_SEND;
+       else
+               dregs->csr &= ~CSR_SEND;
+       
+       /* byte count for fifo */
+       dregs->fifo_count = count;
+
+       sun3_udc_write(UDC_RESET, UDC_CSR);
+       
+       /* reset fifo */
+       dregs->csr &= ~CSR_FIFO;
+       dregs->csr |= CSR_FIFO;
+       
+
+       if(dregs->fifo_count != count) { 
+               printk("scsi%d: fifo_mismatch %04x not %04x\n",
+                      default_instance->host_no, dregs->fifo_count,
+                      (unsigned int) count);
+               NCR5380_print(default_instance);
+       }
+
+       /* setup udc */
+       udc_regs->addr_hi = ((sun3_dvma_vtop(dmabuf) & 0xff0000) >> 8);
+       udc_regs->addr_lo = (sun3_dvma_vtop(dmabuf) & 0xffff);
+       udc_regs->count = count/2; /* count in words */
+       udc_regs->mode_hi = UDC_MODE_HIWORD;
+       if(write_flag) {
+               if(count & 1)
+                       udc_regs->count++;
+               udc_regs->mode_lo = UDC_MODE_LSEND;
+               udc_regs->rsel = UDC_RSEL_SEND;
+       } else {
+               udc_regs->mode_lo = UDC_MODE_LRECV;
+               udc_regs->rsel = UDC_RSEL_RECV;
        }
+       
+       /* announce location of regs block */
+       sun3_udc_write(((sun3_dvma_vtop(udc_regs) & 0xff0000) >> 8),
+                      UDC_CHN_HI); 
+
+       sun3_udc_write((sun3_dvma_vtop(udc_regs) & 0xffff), UDC_CHN_LO);
+
+       /* set dma master on */
+       sun3_udc_write(0xd, UDC_MODE);
+
+       /* interrupt enable */
+       sun3_udc_write(UDC_INT_ENABLE, UDC_CSR);
+       
+               return count;
+
+}
+
+static inline unsigned long sun3scsi_dma_residual(struct Scsi_Host *instance)
+{
+       unsigned short resid;
+
+       dregs->udc_addr = 0x32; 
+       udelay(SUN3_DMA_DELAY);
+       resid = dregs->udc_data;
+       udelay(SUN3_DMA_DELAY);
+       resid *= 2;
+
+       return (unsigned long) resid;
+}
+
+static inline unsigned long sun3scsi_dma_xfer_len(unsigned long wanted, Scsi_Cmnd *cmd,
+                                   int write_flag)
+{
+               return wanted;
 }
 
+/* clean up after our dma is done */
+static int sun3scsi_dma_finish(void)
+{
+       unsigned short count;
+       int ret = 0;
+       
+       count = sun3scsi_dma_residual(default_instance);
+
+       sun3_dma_active = 0;
+
+       /* if we've finished a read, copy out the data we read */
+       if(sun3_dma_orig_addr) {
+               /* check for residual bytes after dma end */
+               if(count && (NCR5380_read(BUS_AND_STATUS_REG) &
+                            (BASR_PHASE_MATCH | BASR_ACK))) {
+                       printk("scsi%d: sun3_scsi_finish: read overrun baby... ", default_instance->host_no);
+                       printk("basr now %02x\n", NCR5380_read(BUS_AND_STATUS_REG));
+                       ret = count;
+               }
+               
+               /* copy in what we dma'd no matter what */
+               memcpy(sun3_dma_orig_addr, dmabuf, sun3_dma_orig_count);
+               sun3_dma_orig_addr = NULL;
+
+       }
+       
+       sun3_udc_write(UDC_RESET, UDC_CSR);
+       
+       dregs->csr &= ~CSR_SEND;
+
+       /* reset fifo */
+       dregs->csr &= ~CSR_FIFO;
+       dregs->csr |= CSR_FIFO;
+       
+       sun3_dma_setup_done = NULL;
+
+       return ret;
+
+}
+       
+#include "sun3_NCR5380.c"
 
 #ifdef MODULE
 
@@ -414,3 +539,4 @@ Scsi_Host_Template driver_template = SUN3_NCR5380;
 
 #include "scsi_module.c"
 #endif
+
index c677bfbf614bd84b18b81d52be5b037f931b2c44..b94f586aa00d4ca9e8c43aa2ec9081891e89cd30 100644 (file)
@@ -1,6 +1,8 @@
 /*
  * Sun3 SCSI stuff by Erik Verbruggen (erik@bigmama.xtdnet.nl)
  *
+ * Sun3 DMA additions by Sam Creasey (sammy@oh.verio.com)
+ *
  * Adapted from mac_scsinew.h:
  */
 /*
@@ -102,18 +104,272 @@ use_clustering:          DISABLE_CLUSTERING                              \
 #define NCR5380_setup(instance) \
         _instance = instance
 
-#define NCR5380_read(reg) sun3scsi_read(_instance, reg)
-#define NCR5380_write(reg, value) sun3scsi_write(_instance, reg, value)
+#define NCR5380_read(reg) sun3scsi_read(reg)
+#define NCR5380_write(reg, value) sun3scsi_write(reg, value)
 
 #define NCR5380_intr sun3scsi_intr
 #define NCR5380_queue_command sun3scsi_queue_command
-#define NCR5380_abort sun3scsi_abort
 #define NCR5380_reset sun3scsi_reset
+#define NCR5380_abort sun3scsi_abort
 #define NCR5380_proc_info sun3scsi_proc_info
+#define NCR5380_dma_xfer_len(i, cmd, phase) \
+        sun3scsi_dma_xfer_len(cmd->SCp.this_residual,cmd,((phase) & SR_IO) ? 0 : 1)
+
+#define NCR5380_dma_write_setup(instance, data, count) sun3scsi_dma_setup(data, count, 1)
+#define NCR5380_dma_read_setup(instance, data, count) sun3scsi_dma_setup(data, count, 0)
+#define NCR5380_dma_residual sun3scsi_dma_residual
 
 #define BOARD_NORMAL   0
 #define BOARD_NCR53C400        1
 
+/* additional registers - mainly DMA control regs */
+/* these start at regbase + 8 -- directly after the NCR regs */
+struct sun3_dma_regs {
+       unsigned short vmeregs[4];  /* unimpl vme stuff */
+       unsigned short udc_data; /* udc dma data reg */
+       unsigned short udc_addr; /* uda dma addr reg */
+       unsigned short fifo_data; /* fifo data reg, holds extra byte on
+                                    odd dma reads */
+       unsigned short fifo_count; 
+       unsigned short csr; /* control/status reg */
+};
+
+/* ucd chip specific regs - live in dvma space */
+struct sun3_udc_regs {
+     unsigned short rsel; /* select regs to load */
+     unsigned short addr_hi; /* high word of addr */
+     unsigned short addr_lo; /* low word */
+     unsigned short count; /* words to be xfer'd */
+     unsigned short mode_hi; /* high word of channel mode */
+     unsigned short mode_lo; /* low word of channel mode */
+};
+
+/* addresses of the udc registers */
+#define UDC_MODE 0x38 
+#define UDC_CSR 0x2e /* command/status */
+#define UDC_CHN_HI 0x26 /* chain high word */
+#define UDC_CHN_LO 0x22 /* chain lo word */
+#define UDC_CURA_HI 0x1a /* cur reg A high */
+#define UDC_CURA_LO 0x0a /* cur reg A low */
+#define UDC_CURB_HI 0x12 /* cur reg B high */
+#define UDC_CURB_LO 0x02 /* cur reg B low */
+#define UDC_MODE_HI 0x56 /* mode reg high */
+#define UDC_MODE_LO 0x52 /* mode reg low */
+#define UDC_COUNT 0x32 /* words to xfer */
+
+/* some udc commands */
+#define UDC_RESET 0
+#define UDC_CHN_START 0xa0 /* start chain */
+#define UDC_INT_ENABLE 0x32 /* channel 1 int on */
+
+/* udc mode words */
+#define UDC_MODE_HIWORD 0x40
+#define UDC_MODE_LSEND 0xc2
+#define UDC_MODE_LRECV 0xd2
+
+/* udc reg selections */
+#define UDC_RSEL_SEND 0x282
+#define UDC_RSEL_RECV 0x182
+
+/* bits in csr reg */
+#define CSR_DMA_ACTIVE 0x8000
+#define CSR_DMA_CONFLICT 0x4000
+#define CSR_DMA_BUSERR 0x2000
+
+#define CSR_FIFO_EMPTY 0x400 /* fifo flushed? */
+#define CSR_SDB_INT 0x200 /* sbc interrupt pending */
+#define CSR_DMA_INT 0x100 /* dma interrupt pending */
+
+#define CSR_SEND 0x8 /* 1 = send  0 = recv */
+#define CSR_FIFO 0x2 /* reset fifo */
+#define CSR_INTR 0x4 /* interrupt enable */
+#define CSR_SCSI 0x1 
+
+// debugging printk's, taken from atari_scsi.h 
+/* Debugging printk definitions:
+ *
+ *  ARB  -> arbitration
+ *  ASEN -> auto-sense
+ *  DMA  -> DMA
+ *  HSH  -> PIO handshake
+ *  INF  -> information transfer
+ *  INI  -> initialization
+ *  INT  -> interrupt
+ *  LNK  -> linked commands
+ *  MAIN -> NCR5380_main() control flow
+ *  NDAT -> no data-out phase
+ *  NWR  -> no write commands
+ *  PIO  -> PIO transfers
+ *  PDMA -> pseudo DMA (unused on Atari)
+ *  QU   -> queues
+ *  RSL  -> reselections
+ *  SEL  -> selections
+ *  USL  -> usleep cpde (unused on Atari)
+ *  LBS  -> last byte sent (unused on Atari)
+ *  RSS  -> restarting of selections
+ *  EXT  -> extended messages
+ *  ABRT -> aborting and resetting
+ *  TAG  -> queue tag handling
+ *  MER  -> merging of consec. buffers
+ *
+ */
+
+
+
+#if NDEBUG & NDEBUG_ARBITRATION
+#define ARB_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define ARB_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_AUTOSENSE
+#define ASEN_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define ASEN_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_DMA
+#define DMA_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define DMA_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_HANDSHAKE
+#define HSH_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define HSH_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INFORMATION
+#define INF_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define INF_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INIT
+#define INI_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define INI_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INTR
+#define INT_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define INT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_LINKED
+#define LNK_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define LNK_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_MAIN
+#define MAIN_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define MAIN_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_NO_DATAOUT
+#define NDAT_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define NDAT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_NO_WRITE
+#define NWR_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define NWR_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_PIO
+#define PIO_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define PIO_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_PSEUDO_DMA
+#define PDMA_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define PDMA_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_QUEUES
+#define QU_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define QU_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_RESELECTION
+#define RSL_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define RSL_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_SELECTION
+#define SEL_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define SEL_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_USLEEP
+#define USL_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define USL_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_LAST_BYTE_SENT
+#define LBS_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define LBS_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_RESTART_SELECT
+#define RSS_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define RSS_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_EXTENDED
+#define EXT_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define EXT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_ABORT
+#define ABRT_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define ABRT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_TAGS
+#define TAG_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define TAG_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_MERGING
+#define MER_PRINTK(format, args...) \
+       printk(KERN_DEBUG format , ## args)
+#else
+#define MER_PRINTK(format, args...)
+#endif
+
+/* conditional macros for NCR5380_print_{,phase,status} */
+
+#define NCR_PRINT(mask)        \
+       ((NDEBUG & (mask)) ? NCR5380_print(instance) : (void)0)
+
+#define NCR_PRINT_PHASE(mask) \
+       ((NDEBUG & (mask)) ? NCR5380_print_phase(instance) : (void)0)
+
+#define NCR_PRINT_STATUS(mask) \
+       ((NDEBUG & (mask)) ? NCR5380_print_status(instance) : (void)0)
+
+#define NDEBUG_ANY     0xffffffff
+
+
+
 #endif /* ndef HOSTS_C */
 #endif /* SUN3_NCR5380_H */
 
index 2ccc8d4bedd42898f6f4231d4d94041a4d2b81e5..b7a855b77f9ccfb39433cf64207470431b2a82ed 100644 (file)
@@ -73,6 +73,8 @@ if [ "$CONFIG_FB" = "y" ]; then
    fi
    if [ "$CONFIG_MAC" = "y" ]; then
       define_bool CONFIG_FB_MAC y
+      bool '  Apple "valkyrie" display support' CONFIG_FB_VALKYRIE
+#      bool '  Apple DAFB display support' CONFIG_FB_DAFB
    fi
    if [ "$CONFIG_HP300" = "y" ]; then
       define_bool CONFIG_FB_HP300 y
@@ -89,6 +91,13 @@ if [ "$CONFIG_FB" = "y" ]; then
       tristate '  SGI Visual Workstation framebuffer support' CONFIG_FB_SGIVW
       define_bool CONFIG_BUS_I2C y
    fi
+   if [ "$CONFIG_SUN3" = "y" -o "$CONFIG_SUN3X" = "y" ]; then
+      bool 'Sun3 framebuffer support' CONFIG_FB_SUN3
+      if [ "$CONFIG_FB_SUN3" != "n" ]; then
+         bool '  BWtwo support' CONFIG_FB_BWTWO
+         bool '  CGsix (GX,TurboGX) support' CONFIG_FB_CGSIX
+      fi
+   fi
    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       if [ "$CONFIG_PCI" != "n" ]; then
         tristate '  Matrox acceleration (EXPERIMENTAL)' CONFIG_FB_MATROX
index 3e31a5fdc9d5db84287a5ca3cfa9190fb41e9a10..e52eee833ef34a5cc47a2bc0fc8d84425c19e581 100644 (file)
@@ -60,7 +60,7 @@ obj-$(CONFIG_FB_CYBER2000)        += cyber2000fb.o
 obj-$(CONFIG_FB_SGIVW)            += sgivwfb.o
 obj-$(CONFIG_FB_RIVA)             += rivafb.o riva_hw.o
 obj-$(CONFIG_FB_3DFX)             += tdfxfb.o
-obj-$(CONFIG_FB_MAC)              += macfb.o
+obj-$(CONFIG_FB_MAC)              += macfb.o macmodes.o
 obj-$(CONFIG_FB_HP300)            += hpfb.o
 obj-$(CONFIG_FB_OF)               += offb.o macmodes.o
 obj-$(CONFIG_FB_IMSTT)            += imsttfb.o
@@ -82,6 +82,8 @@ obj-$(CONFIG_FB_CGFOURTEEN)       += cgfourteenfb.o sbusfb.o
 obj-$(CONFIG_FB_P9100)            += p9100fb.o sbusfb.o
 obj-$(CONFIG_FB_LEO)              += leofb.o sbusfb.o
 obj-$(CONFIG_FB_MATROX)           += matroxfb.o
+obj-$(CONFIG_FB_SUN3)             += sun3fb.o
+obj-$(CONFIG_FB_BWTWO)            += bwtwofb.o
 obj-$(CONFIG_FB_VIRTUAL)          += vfb.o  
 
 # Generic Low Level Drivers
index f8c0ea52d64297ccd2315459a98b5e6dd00f78b5..f40b873bab4685580eb67753dd47c2bfccacb8dd 100644 (file)
@@ -25,7 +25,7 @@
 
 #include <video/sbusfb.h>
 #include <asm/io.h>
-#ifndef __sparc_v9__
+#if !defined(__sparc_v9__) && !defined(__mc68000__)
 #include <asm/sun4paddr.h>
 #endif
 
@@ -214,9 +214,11 @@ char __init *bwtwofb_init(struct fb_info_sbusfb *fb)
                                case BWTWO_SR_ID_NOCONN:
                                        return NULL;
                                default:
+#ifndef CONFIG_FB_SUN3
                                        prom_printf("bw2: can't handle SR %02x\n",
                                                    status);
                                        prom_halt();
+#endif                                 
                                        return NULL; /* fool gcc. */
                        }
                        for ( ; *p; p += 2) {
index 77adb63956fa9d50b12a9ae3540a79a11a07e588..0b3e816e71607b2d45ded697eab29778707b6e65 100644 (file)
@@ -331,7 +331,8 @@ static void plot_pixel_mac(struct display *p, int bw, int pixel_x, int pixel_y)
   u16 *dest16, pix16;
   u32 *dest32, pix32;
 
-  if (pixel_x < 0 || pixel_y < 0 || pixel_x >= 832 || pixel_y >= 624) {
+  /* There *are* 68k Macs that support more than 832x624, you know :-) */
+  if (pixel_x < 0 || pixel_y < 0 || pixel_x >= p->var.xres || pixel_y >= p->var.yres) {
     int cnt;
     printk ("ERROR: pixel_x == %d, pixel_y == %d", pixel_x, pixel_y);
     for(cnt = 0; cnt < 100000; cnt++)
index 7969042925ebdf14976c5ff7d603908a467ce4f8..00d21faad9f2cfb119657e9e1fdd070e6ed5c874 100644 (file)
@@ -98,6 +98,8 @@ extern int g364fb_init(void);
 extern int fm2fb_init(void);
 extern int fm2fb_setup(char*);
 extern int q40fb_init(void);
+extern int sun3fb_init(void);
+extern int sun3fb_setup(char *);
 extern int sgivwfb_init(void);
 extern int sgivwfb_setup(char*);
 extern int rivafb_init(void);
@@ -211,6 +213,9 @@ static struct {
 #ifdef CONFIG_FB_FM2
        { "fm2fb", fm2fb_init, fm2fb_setup },
 #endif 
+#ifdef CONFIG_FB_SUN3
+       { "sun3", sun3fb_init, sun3fb_setup },
+#endif
 #ifdef CONFIG_GSP_RESOLVER
        /* Not a real frame buffer device... */
        { "resolver", NULL, resolver_video_setup },
@@ -471,6 +476,9 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
        vma->vm_flags |= VM_IO;
 #else
 #if defined(__mc68000__)
+#if defined(CONFIG_SUN3)
+       pgprot_val(vma->vm_page_prot) |= SUN3_PAGE_NOCACHE;
+#else
        if (CPU_IS_020_OR_030)
                pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE030;
        if (CPU_IS_040_OR_060) {
@@ -478,6 +486,7 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
                /* Use no-cache mode, serialized */
                pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE_S;
        }
+#endif
 #elif defined(__powerpc__)
        pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE|_PAGE_GUARDED;
 #elif defined(__alpha__)
index 282c47739c13f2a9cbddf0dc3ef005e549dbb936..37a44ea5a61a32335202459d8907a4bbe38533a0 100644 (file)
@@ -1,6 +1,22 @@
-/*
- *     We've been given MAC frame buffer info by the booter. Now go set it up
- */
+/* macfb.c: Generic framebuffer for Macs whose colourmaps/modes we
+   don't know how to set */
+
+/* (c) 1999 David Huggins-Daines <dhd@debian.org>
+
+   Primarily based on vesafb.c, by Gerd Knorr
+   (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
+
+   Also uses information and code from:
+   
+   The original macfb.c from Linux/mac68k 2.0, by Alan Cox, Juergen
+   Mellinger, Mikael Forselius, Michael Schmitz, and others.
+
+   valkyriefb.c, by Martin Costabel, Kevin Schoedel, Barry Nathan, Dan
+   Jacobowitz, Paul Mackerras, Fabio Riccardi, and Geert Uytterhoeven.
+   
+   This code is free software.  You may copy, modify, and distribute
+   it subject to the terms and conditions of the GNU General Public
+   License, version 2, or any later version, at your convenience. */
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -13,6 +29,7 @@
 #include <linux/delay.h>
 #include <linux/nubus.h>
 #include <linux/init.h>
+#include <linux/fb.h>
 
 #include <asm/setup.h>
 #include <asm/bootinfo.h>
 #include <asm/pgtable.h>
 #include <asm/irq.h>
 #include <asm/macintosh.h>
-#include <linux/fb.h>
+#include <asm/io.h>
+#include <asm/machw.h>
 
 #include <video/fbcon.h>
-/* conditionalize these ?? */
 #include <video/fbcon-mfb.h>
 #include <video/fbcon-cfb2.h>
 #include <video/fbcon-cfb4.h>
 #include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb24.h>
+#include <video/fbcon-cfb32.h>
+
+#if defined(FBCON_HAS_CFB8) || defined(FBCON_HAS_CFB4) || defined(FBCON_HAS_CFB2)
+
+/* Common DAC base address for the LC, RBV, Valkyrie, and IIvx */
+#define DAC_BASE 0x50f24000
+
+/* Some addresses for the DAFB */
+#define DAFB_BASE 0xf9800200
+
+/* Address for the built-in Civic framebuffer in Quadra AVs */
+#define CIVIC_BASE 0x50f30800  /* Only tested on 660AV! */
+
+/* GSC (Gray Scale Controller) base address */
+#define GSC_BASE 0x50F20000
+
+/* CSC (Color Screen Controller) base address */
+#define CSC_BASE 0x50F20000
+
+static int (*macfb_setpalette) (unsigned int regno, unsigned int red,
+                               unsigned int green, unsigned int blue) = NULL;
+static int valkyrie_setpalette (unsigned int regno, unsigned int red,
+                               unsigned int green, unsigned int blue);
+static int dafb_setpalette (unsigned int regno, unsigned int red,
+                           unsigned int green, unsigned int blue);
+static int rbv_setpalette (unsigned int regno, unsigned int red,
+                          unsigned int green, unsigned int blue);
+static int mdc_setpalette (unsigned int regno, unsigned int red,
+                          unsigned int green, unsigned int blue);
+static int toby_setpalette (unsigned int regno, unsigned int red,
+                           unsigned int green, unsigned int blue);
+static int civic_setpalette (unsigned int regno, unsigned int red,
+                            unsigned int green, unsigned int blue);
+static int csc_setpalette (unsigned int regno, unsigned int red,
+                            unsigned int green, unsigned int blue);
+
+static volatile struct {
+       unsigned char addr;
+       /* Note: word-aligned */
+       char pad[3];
+       unsigned char lut;
+} *valkyrie_cmap_regs;
+
+static volatile struct {
+       unsigned char addr;
+       unsigned char lut;
+} *v8_brazil_cmap_regs;
+
+static volatile struct {
+       unsigned char addr;
+       char pad1[3]; /* word aligned */
+       unsigned char lut;
+       char pad2[3]; /* word aligned */
+       unsigned char cntl; /* a guess as to purpose */
+} *rbv_cmap_regs;
+
+static volatile struct {
+       unsigned long reset;
+       unsigned long pad1[3];
+       unsigned char pad2[3];
+       unsigned char lut;
+} *dafb_cmap_regs;
+
+static volatile struct {
+       unsigned char addr;     /* OFFSET: 0x00 */
+       unsigned char pad1[15];
+       unsigned char lut;      /* OFFSET: 0x10 */
+       unsigned char pad2[15];
+       unsigned char status;   /* OFFSET: 0x20 */
+       unsigned char pad3[7];
+       unsigned long vbl_addr; /* OFFSET: 0x28 */
+       unsigned int  status2;  /* OFFSET: 0x2C */
+} *civic_cmap_regs;
+
+static volatile struct {
+       char    pad1[0x40];
+        unsigned char  clut_waddr;     /* 0x40 */
+        char    pad2;
+        unsigned char  clut_data;      /* 0x42 */
+        char   pad3[0x3];
+        unsigned char  clut_raddr;     /* 0x46 */
+} *csc_cmap_regs;
+
+/* We will leave these the way they are for the time being */
+struct mdc_cmap_regs {
+       char pad1[0x200200];
+       unsigned char addr;
+       char pad2[6];
+       unsigned char lut;
+};
+
+struct toby_cmap_regs {
+       char pad1[0x90018];
+       unsigned char lut; /* TFBClutWDataReg, offset 0x90018 */
+       char pad2[3];
+       unsigned char addr; /* TFBClutAddrReg, offset 0x9001C */
+};
+
+struct jet_cmap_regs {
+       char pad1[0xe0e000];
+       unsigned char addr;
+       unsigned char lut;
+};
+
+#endif
+
+#define PIXEL_TO_MM(a) (((a)*10)/28)   /* width in mm at 72 dpi */     
+
+static char* video_base;
+static int   video_size;
+static char* video_vbase;        /* mapped */
 
-#define arraysize(x)    (sizeof(x)/sizeof(*(x)))
+/* mode */
+static int  video_bpp;
+static int  video_width;
+static int  video_height;
+static int  video_type = FB_TYPE_PACKED_PIXELS;
+static int  video_visual;
+static int  video_linelength;
+static int  video_cmap_len;
+static int  video_slot = 0;
 
 static struct fb_var_screeninfo macfb_defined={
        0,0,0,0,        /* W,H, W, H (virtual) load xres,xres_virtual*/
@@ -42,38 +180,36 @@ static struct fb_var_screeninfo macfb_defined={
        {0,0,0},        /* transparency */
        0,              /* standard pixel format */
        FB_ACTIVATE_NOW,
-       274,195,        /* 14" monitor *Mikael Nykvist's anyway* */
-       0,              /* The only way to accelerate a mac is .. */
+       -1, -1,
+       FB_ACCEL_NONE,  /* The only way to accelerate a mac is .. */
        0L,0L,0L,0L,0L,
        0L,0L,0,        /* No sync info */
        FB_VMODE_NONINTERLACED,
        {0,0,0,0,0,0}
 };
 
-#define NUM_TOTAL_MODES                1
-#define NUM_PREDEF_MODES       1
-
 static struct display disp;
 static struct fb_info fb_info;
+static struct { u_short blue, green, red, pad; } palette[256];
+static union {
+#ifdef FBCON_HAS_CFB16
+    u16 cfb16[16];
+#endif
+#ifdef FBCON_HAS_CFB24
+    u32 cfb24[16];
+#endif
+#ifdef FBCON_HAS_CFB32
+    u32 cfb32[16];
+#endif
+} fbcon_cmap;
 
-static int inverse = 0;
-
-struct macfb_par
-{
-       void *unused;
-};
-
-static int currcon = 0;
-static int current_par_valid = 0;
-struct macfb_par current_par;
-
-static int mac_xres,mac_yres,mac_depth, mac_xbytes, mac_vxres;
-static unsigned long mac_videobase;
-static unsigned long mac_videosize;
+static int             inverse   = 0;
+static int             vidtest   = 0;
+static int             currcon   = 0;
 
-       /*
       * Open/Release the frame buffer device
       */
+/*
+ * Open/Release the frame buffer device
+ */
 
 static int macfb_open(struct fb_info *info, int user)
 {
@@ -90,105 +226,32 @@ static int macfb_release(struct fb_info *info, int user)
        return(0);
 }
 
-static void macfb_encode_var(struct fb_var_screeninfo *var, 
-                               struct macfb_par *par)
-{
-       memset(var, 0, sizeof(struct fb_var_screeninfo));
-       var->xres=mac_xres;
-       var->yres=mac_yres;
-       var->xres_virtual=mac_vxres;
-       var->yres_virtual=var->yres;
-       var->xoffset=0;
-       var->yoffset=0;
-       var->bits_per_pixel = mac_depth;
-       var->grayscale=0;
-       var->transp.offset=0;
-       var->transp.length=0;
-       var->transp.msb_right=0;
-       var->nonstd=0;
-       var->activate=0;
-       var->height= -1;
-       var->width= -1;
-       var->vmode=FB_VMODE_NONINTERLACED;
-       var->pixclock=0;
-       var->sync=0;
-       var->left_margin=0;
-       var->right_margin=0;
-       var->upper_margin=0;
-       var->lower_margin=0;
-       var->hsync_len=0;
-       var->vsync_len=0;
-       return;
-}
-
-
-static void macfb_get_par(struct macfb_par *par)
-{
-       *par=current_par;
-}
-
-static void macfb_set_par(struct macfb_par *par)
-{
-       current_par_valid=1;
-}
-
-static int fb_update_var(int con, struct fb_info *info)
+static int macfb_update_var(int con, struct fb_info *info)
 {
        return 0;
 }
 
-static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
-{
-       struct macfb_par par;
-       
-       macfb_get_par(&par);
-       macfb_encode_var(var, &par);
-       return 0;
-}
-
-extern int console_loglevel;
-
-static void macfb_encode_fix(struct fb_fix_screeninfo *fix, 
-                               struct macfb_par *par)
-{
-       memset(fix, 0, sizeof(struct fb_fix_screeninfo));
-       strcpy(fix->id,"Macintosh");
-
-       /*
-        * fbmem.c accepts non page aligned mappings now!
-        */
-       fix->smem_start=mac_videobase;
-       fix->smem_len=mac_videosize;
-       fix->type = FB_TYPE_PACKED_PIXELS;
-       if (mac_depth == 1)
-               fix->visual = FB_VISUAL_MONO01;
-       else
-               fix->visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
-       fix->xpanstep=0;
-       fix->ypanstep=0;
-       fix->ywrapstep=0;
-       fix->line_length=mac_xbytes;
-       return;
-}
-
 static int macfb_get_fix(struct fb_fix_screeninfo *fix, int con,
                         struct fb_info *info)
 {
-       struct macfb_par par;
-       macfb_get_par(&par);
-       macfb_encode_fix(fix, &par);
+       memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+       strcpy(fix->id, "Mac Generic");
+
+       fix->smem_start = video_base;
+       fix->smem_len = video_size;
+       fix->type = video_type;
+       fix->visual = video_visual;
+       fix->xpanstep = 0;
+       fix->ypanstep = 0;
+       fix->line_length=video_linelength;
        return 0;
 }
 
 static int macfb_get_var(struct fb_var_screeninfo *var, int con,
                         struct fb_info *info)
 {
-       struct macfb_par par;
        if(con==-1)
-       {
-               macfb_get_par(&par);
-               macfb_encode_var(var, &par);
-       }
+               memcpy(var, &macfb_defined, sizeof(struct fb_var_screeninfo));
        else
                *var=fb_display[con].var;
        return 0;
@@ -204,9 +267,10 @@ static void macfb_set_disp(int con)
        else
                display = &disp;        /* used during initialization */
 
-       macfb_get_fix(&fix, con, 0);
+       macfb_get_fix(&fix, con, &fb_info);
 
-       display->screen_base = fix.smem_start;
+       memset(display, 0, sizeof(struct display));
+       display->screen_base = video_vbase;
        display->visual = fix.visual;
        display->type = fix.type;
        display->type_aux = fix.type_aux;
@@ -215,235 +279,586 @@ static void macfb_set_disp(int con)
        display->line_length = fix.line_length;
        display->next_line = fix.line_length;
        display->can_soft_blank = 0;
-       display->inverse =
-           (fix.visual == FB_VISUAL_MONO01 ? !inverse : inverse);
+       display->inverse = inverse;
+       display->scrollmode = SCROLL_YREDRAW;
+       macfb_get_var(&display->var, -1, &fb_info);
 
-       switch (mac_depth) {
+       switch (video_bpp) {
 #ifdef FBCON_HAS_MFB
-           case 1:
+       case 1:
                display->dispsw = &fbcon_mfb;
                break;
 #endif
 #ifdef FBCON_HAS_CFB2
-           case 2:
+       case 2:
                display->dispsw = &fbcon_cfb2;
                break;
 #endif
 #ifdef FBCON_HAS_CFB4
-           case 4:
+       case 4:
                display->dispsw = &fbcon_cfb4;
                break;
 #endif
 #ifdef FBCON_HAS_CFB8
-           case 8:
+       case 8:
                display->dispsw = &fbcon_cfb8;
                break;
 #endif
-           default:
-               display->dispsw = &fbcon_dummy;
+#ifdef FBCON_HAS_CFB16
+       case 15:
+       case 16:
+               display->dispsw = &fbcon_cfb16;
+               display->dispsw_data = fbcon_cmap.cfb16;
+               break;
+#endif
+#ifdef FBCON_HAS_CFB24
+       case 24:
+               display->dispsw = &fbcon_cfb24;
+               display->dispsw_data = fbcon_cmap.cfb24;
                break;
+#endif
+#ifdef FBCON_HAS_CFB32
+       case 32:
+               display->dispsw = &fbcon_cfb32;
+               display->dispsw_data = fbcon_cmap.cfb32;
+               break;
+#endif
+       default:
+               display->dispsw = &fbcon_dummy;
+               return;
        }
 }
 
 static int macfb_set_var(struct fb_var_screeninfo *var, int con,
                         struct fb_info *info)
 {
-       int err;
+       static int first = 1;
+
+       if (var->xres           != macfb_defined.xres           ||
+           var->yres           != macfb_defined.yres           ||
+           var->xres_virtual   != macfb_defined.xres_virtual   ||
+           var->yres_virtual   != macfb_defined.yres           ||
+           var->xoffset                                        ||
+           var->bits_per_pixel != macfb_defined.bits_per_pixel ||
+           var->nonstd) {
+               if (first) {
+                       printk("macfb does not support changing the video mode\n");
+                       first = 0;
+               }
+               return -EINVAL;
+       }
+
+       if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST)
+               return 0;
+
+       if (var->yoffset)
+               return -EINVAL;
+       return 0;
+}
+
+#if defined(FBCON_HAS_CFB8) || defined(FBCON_HAS_CFB4) || defined(FBCON_HAS_CFB2)
+static int valkyrie_setpalette (unsigned int regno, unsigned int red,
+                               unsigned int green, unsigned int blue)
+{
+       unsigned long flags;
+       
+       red >>= 8;
+       green >>= 8;
+       blue >>= 8;
+
+       save_flags(flags);
+       cli();
+       
+       /* tell clut which address to fill */
+       writeb(regno, &valkyrie_cmap_regs->addr);
+       nop();
+
+       /* send one color channel at a time */
+       writeb(red, &valkyrie_cmap_regs->lut);
+       nop();
+       writeb(green, &valkyrie_cmap_regs->lut);
+       nop();
+       writeb(blue, &valkyrie_cmap_regs->lut);
+
+       restore_flags(flags);
+
+       return 0;
+}
+
+/* Unlike the Valkyrie, the DAFB cannot set individual colormap
+   registers.  Therefore, we do what the MacOS driver does (no
+   kidding!) and simply set them one by one until we hit the one we
+   want. */
+static int dafb_setpalette (unsigned int regno, unsigned int red,
+                           unsigned int green, unsigned int blue)
+{
+       /* FIXME: really, really need to use ioremap() here,
+           phys_to_virt() doesn't work anymore */
+       static int lastreg = -1;
+       unsigned long flags;
+       
+       red >>= 8;
+       green >>= 8;
+       blue >>= 8;
+
+       save_flags(flags);
+       cli();
+       
+       /* fbcon will set an entire colourmap, but X won't.  Hopefully
+          this should accomodate both of them */
+       if (regno != lastreg+1) {
+               int i;
+               
+               /* Stab in the dark trying to reset the CLUT pointer */
+               writel(0, &dafb_cmap_regs->reset);
+               nop();
+               
+               /* Loop until we get to the register we want */
+               for (i = 0; i < regno; i++) {
+                       writeb(palette[i].red >> 8, &dafb_cmap_regs->lut);
+                       nop();
+                       writeb(palette[i].green >> 8, &dafb_cmap_regs->lut);
+                       nop();
+                       writeb(palette[i].blue >> 8, &dafb_cmap_regs->lut);
+                       nop();
+               }
+       }
+               
+       writeb(red, &dafb_cmap_regs->lut);
+       nop();
+       writeb(green, &dafb_cmap_regs->lut);
+       nop();
+       writeb(blue, &dafb_cmap_regs->lut);
+       
+       restore_flags(flags);
+       
+       lastreg = regno;
+       return 0;
+}
+
+/* V8 and Brazil seem to use the same DAC.  Sonora does as well. */
+static int v8_brazil_setpalette (unsigned int regno, unsigned int red,
+                                unsigned int green, unsigned int blue)
+{
+       unsigned char _red  =red>>8;
+       unsigned char _green=green>>8;
+       unsigned char _blue =blue>>8;
+       unsigned char _regno;
+       unsigned long flags;
+
+       if (video_bpp>8) return 1; /* failsafe */
+
+       save_flags(flags);
+       cli();
+
+       /* On these chips, the CLUT register numbers are spread out
+          across the register space.  Thus:
+
+          In 8bpp, all regnos are valid.
+          
+          In 4bpp, the regnos are 0x0f, 0x1f, 0x2f, etc, etc
+          
+          In 2bpp, the regnos are 0x3f, 0x7f, 0xbf, 0xff */
+       _regno = (regno<<(8-video_bpp)) | (0xFF>>video_bpp);
+       writeb(_regno, &v8_brazil_cmap_regs->addr); nop();
+
+       /* send one color channel at a time */
+       writeb(_red, &v8_brazil_cmap_regs->lut); nop();
+       writeb(_green, &v8_brazil_cmap_regs->lut); nop();
+       writeb(_blue, &v8_brazil_cmap_regs->lut);
+
+       restore_flags(flags);
+       
+       return 0;
+}
+
+static int rbv_setpalette (unsigned int regno, unsigned int red,
+                          unsigned int green, unsigned int blue)
+{
+       /* use MSBs */
+       unsigned char _red  =red>>8;
+       unsigned char _green=green>>8;
+       unsigned char _blue =blue>>8;
+       unsigned char _regno;
+       unsigned long flags;
+
+       if (video_bpp>8) return 1; /* failsafe */
+
+       save_flags(flags);
+       cli();
+       
+       /* From the VideoToolbox driver.  Seems to be saying that
+        * regno #254 and #255 are the important ones for 1-bit color,
+        * regno #252-255 are the important ones for 2-bit color, etc.
+        */
+       _regno = regno + (256-(1<<video_bpp));
+
+       /* reset clut? (VideoToolbox sez "not necessary") */
+       writeb(0xFF, &rbv_cmap_regs->cntl); nop();
+       
+       /* tell clut which address to use. */
+       writeb(_regno, &rbv_cmap_regs->addr); nop();
+       
+       /* send one color channel at a time. */
+       writeb(_red,   &rbv_cmap_regs->lut); nop();
+       writeb(_green, &rbv_cmap_regs->lut); nop();
+       writeb(_blue,  &rbv_cmap_regs->lut);
+       
+       restore_flags(flags);
+       /* done. */
+       return 0;
+}
+
+/* Macintosh Display Card (8x24) */
+static int mdc_setpalette(unsigned int regno, unsigned int red,
+                         unsigned int green, unsigned int blue)
+{
+       volatile struct mdc_cmap_regs *cmap_regs =
+               nubus_slot_addr(video_slot);
+       /* use MSBs */
+       unsigned char _red  =red>>8;
+       unsigned char _green=green>>8;
+       unsigned char _blue =blue>>8;
+       unsigned char _regno=regno;
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       
+       /* the nop's are there to order writes. */
+       writeb(_regno, &cmap_regs->addr); nop();
+       writeb(_red, &cmap_regs->lut);    nop();
+       writeb(_green, &cmap_regs->lut);  nop();
+       writeb(_blue, &cmap_regs->lut);
+
+       restore_flags(flags);
+       return 0;
+}
+
+/* Toby frame buffer */
+static int toby_setpalette(unsigned int regno, unsigned int red,
+                          unsigned int green, unsigned int blue)
+{
+       volatile struct toby_cmap_regs *cmap_regs =
+               nubus_slot_addr(video_slot);
+       /* use MSBs */
+       unsigned char _red  =~(red>>8);
+       unsigned char _green=~(green>>8);
+       unsigned char _blue =~(blue>>8);
+       unsigned char _regno = (regno<<(8-video_bpp)) | (0xFF>>video_bpp);
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
        
-       if ((err=do_fb_set_var(var, 1)))
-               return err;
+       writeb(_regno, &cmap_regs->addr); nop();
+       writeb(_red, &cmap_regs->lut);    nop();
+       writeb(_green, &cmap_regs->lut);  nop();
+       writeb(_blue, &cmap_regs->lut);
+
+       restore_flags(flags);
+       return 0;
+}
+
+/* Jet frame buffer */
+static int jet_setpalette(unsigned int regno, unsigned int red,
+                         unsigned int green, unsigned int blue)
+{
+       volatile struct jet_cmap_regs *cmap_regs =
+               nubus_slot_addr(video_slot);
+       /* use MSBs */
+       unsigned char _red   = (red>>8);
+       unsigned char _green = (green>>8);
+       unsigned char _blue  = (blue>>8);
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       
+       writeb(regno, &cmap_regs->addr); nop();
+       writeb(_red, &cmap_regs->lut); nop();
+       writeb(_green, &cmap_regs->lut); nop();
+       writeb(_blue, &cmap_regs->lut);
+
+       restore_flags(flags);
        return 0;
 }
 
 /*
- * Color map handling - hardcoded maps!! 
+ * Civic framebuffer -- Quadra AV built-in video.  A chip
+ * called Sebastian holds the actual color palettes, and
+ * apparently, there are two different banks of 512K RAM 
+ * which can act as separate framebuffers for doing video
+ * input and viewing the screen at the same time!  The 840AV
+ * Can add another 1MB RAM to give the two framebuffers 
+ * 1MB RAM apiece.
  *
- * 2.0 color map primitives, copied from atafb.c
- */
-/*
- * should be kmalloc'ed on request
+ * FIXME: this doesn't seem to work anymore.
  */
-static short red256[256], green256[256], blue256[256];
-
-static short red16[]=
-       { 0x0000,0x0000,0x0000,0x0000,0x8080,0x8080,0x8080,0xc0c0,
-         0x8080,0x0000,0x0000,0x0000,0xffff,0xffff,0xffff,0xffff};
-static short green16[]=
-       { 0x0000,0x0000,0x8080,0x8080,0x0000,0x0000,0x8080,0xc0c0,
-         0x8080,0x0000,0xffff,0xffff,0x0000,0x0000,0xffff,0xffff};
-static short blue16[]=
-       { 0x0000,0x8080,0x0000,0x8080,0x0000,0x8080,0x0000,0xc0c0,
-         0x8080,0xffff,0x0000,0xffff,0x0000,0xffff,0x0000,0xffff};
-
-static short red4[]=
-       { 0x0000,0x8080,0xffff,0xffff};
-static short green4[]=
-       { 0x0000,0x8080,0x0000,0xffff};
-static short blue4[]=
-       { 0x0000,0x8080,0x0000,0xffff};
-
-static short red2[]=
-       { 0x0000,0xffff};
-static short green2[]=
-       { 0x0000,0xffff};
-static short blue2[]=
-       { 0x0000,0xffff};
-
-struct fb_cmap default_256_colors = { 0, 256, red256, green256, blue256, NULL };
-struct fb_cmap default_16_colors  = { 0, 16, red16, green16, blue16, NULL };
-struct fb_cmap default_4_colors   = { 0, 4, red4, green4, blue4, NULL };
-struct fb_cmap default_2_colors   = { 0, 2, red2, green2, blue2, NULL };
-
-static int mac_set_cmap256(struct fb_cmap* cmap)
+static int civic_setpalette (unsigned int regno, unsigned int red,
+                            unsigned int green, unsigned int blue)
 {
-       int i,start;
-       unsigned short *red,*green,*blue;
-       unsigned short cval[] = {0xffff, 0xcccc, 0x9999, 
-                                0x6666, 0x3333, 0x0000 };
-       unsigned short gval[] = {0x0a0a, 0x1414, 0x1e1e,
-                                0x2828, 0x3232, 0x3c3c,
-                                0x4646, 0x5050, 0x5a5a,
-                                0x6464, 0x6e6e, 0x7878,
-                                0x8282, 0x8c8c, 0x9696,
-                                0xa0a0, 0xaaaa, 0xb4b4,
-                                0xbebe, 0xc8c8, 0xd2d2,
-                                0xdcdc, 0xe6e6, 0xf0f0};
-
-       red=cmap->red;
-       green=cmap->green;
-       blue=cmap->blue;
-       start=cmap->start;
-
-       if (start < 0)
-               return -EINVAL;
-       if (cmap->len < 255)
-               return -EINVAL;
-       /* 16 ANSI colors */
-       for (i=0 ; i < 16 ; i++) {
-               *red++   = red16[i];
-               *green++ = green16[i];
-               *blue++  = blue16[i];
+       static int lastreg = -1;
+       unsigned long flags;
+       int clut_status;
+       
+       if (video_bpp > 8) return 1; /* failsafe */
+
+       red   >>= 8;
+       green >>= 8;
+       blue  >>= 8;
+
+       save_flags(flags);
+       cli();
+       
+       /*
+        * Set the register address
+        */
+       writeb(regno, &civic_cmap_regs->addr); nop();
+
+       /*
+        * Wait for VBL interrupt here;
+        * They're usually not enabled from Penguin, so we won't check
+        */
+#if 0
+       {
+#define CIVIC_VBL_OFFSET       0x120
+               volatile unsigned long *vbl = readl(civic_cmap_regs->vbl_addr + CIVIC_VBL_OFFSET);
+               /* do interrupt setup stuff here? */
+               *vbl = 0L; nop();       /* clear */
+               *vbl = 1L; nop();       /* set */
+               while (*vbl != 0L)      /* wait for next vbl */
+               {
+                       usleep(10);     /* needed? */
+               }
+               /* do interrupt shutdown stuff here? */
        }
-       /* 216 colors (6x6x6) map) */
-       for (i=16 ; i < 232 ; i++) {
-               *red++   = cval[(i-16)/36];
-               *green++ = cval[((i-16)/6)%6];
-               *blue++  = cval[(i-16)%6];
+#endif
+
+       /*
+        * Grab a status word and do some checking;
+        * Then finally write the clut!
+        */
+       clut_status =  readb(&civic_cmap_regs->status2);
+
+       if ((clut_status & 0x0008) == 0)
+       {
+#if 0
+               if ((clut_status & 0x000D) != 0)
+               {
+                       writeb(0x00, &civic_cmap_regs->lut); nop();
+                       writeb(0x00, &civic_cmap_regs->lut); nop();
+               }
+#endif
+
+               writeb(  red, &civic_cmap_regs->lut); nop();
+               writeb(green, &civic_cmap_regs->lut); nop();
+               writeb( blue, &civic_cmap_regs->lut); nop();
+               writeb( 0x00, &civic_cmap_regs->lut); nop();
        }
-       /* 24 grays */
-       for (i=232 ; i < 256 ; i++) {
-               *red = *green = *blue = gval[i-232];
-               red++;
-               green++;
-               blue++;
+       else
+       {
+               unsigned char junk;
+
+               junk = readb(&civic_cmap_regs->lut); nop();
+               junk = readb(&civic_cmap_regs->lut); nop();
+               junk = readb(&civic_cmap_regs->lut); nop();
+               junk = readb(&civic_cmap_regs->lut); nop();
+
+               if ((clut_status & 0x000D) != 0)
+               {
+                       writeb(0x00, &civic_cmap_regs->lut); nop();
+                       writeb(0x00, &civic_cmap_regs->lut); nop();
+               }
+
+               writeb(  red, &civic_cmap_regs->lut); nop();
+               writeb(green, &civic_cmap_regs->lut); nop();
+               writeb( blue, &civic_cmap_regs->lut); nop();
+               writeb( junk, &civic_cmap_regs->lut); nop();
        }
+
+       restore_flags(flags);
+
+       lastreg = regno;
        return 0;
 }
 
-static struct fb_cmap * mac_get_default_cmap(int bpp)
+/*
+ * The CSC is the framebuffer on the PowerBook 190 series
+ * (and the 5300 too, but that's a PowerMac). This function
+ * brought to you in part by the ECSC driver for MkLinux.
+ */
+
+static int csc_setpalette (unsigned int regno, unsigned int red,
+                            unsigned int green, unsigned int blue)
 {
-       if (bpp == 1)
-               return &default_2_colors;
-       if (bpp == 2)
-               return &default_4_colors;
-       if (bpp == 4)
-               return &default_16_colors;
-       return &default_256_colors;
+       mdelay(1);
+       csc_cmap_regs->clut_waddr = regno;
+       csc_cmap_regs->clut_data = red;
+       csc_cmap_regs->clut_data = green;
+       csc_cmap_regs->clut_data = blue;
+       return 0;
 }
 
-static void memcpy_fs(int fsfromto, void *to, void *from, int len)
+#endif /* FBCON_HAS_CFB8 || FBCON_HAS_CFB4 || FBCON_HAS_CFB2 */
+
+static int macfb_getcolreg(unsigned regno, unsigned *red, unsigned *green,
+                          unsigned *blue, unsigned *transp,
+                          struct fb_info *fb_info)
 {
-    switch (fsfromto) {
-       case 0:
-           memcpy(to, from, len);
-           return;
-       case 1:
-           copy_from_user(to, from, len);
-           return;
-       case 2:
-           copy_to_user(to, from, len);
-           return;
-    }
+       /*
+        *  Read a single color register and split it into colors/transparent.
+        *  Return != 0 for invalid regno.
+        */
+
+       if (regno >= video_cmap_len)
+               return 1;
+
+       *red   = palette[regno].red;
+       *green = palette[regno].green;
+       *blue  = palette[regno].blue;
+       *transp = 0;
+       return 0;
 }
 
-static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto)
+static int macfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+                          unsigned blue, unsigned transp,
+                          struct fb_info *fb_info)
 {
-       int size;
-       int tooff=0, fromoff=0;
+       /*
+        *  Set a single color register. The values supplied are
+        *  already rounded down to the hardware's capabilities
+        *  (according to the entries in the `var' structure). Return
+        *  != 0 for invalid regno.
+        */
+       
+       if (regno >= video_cmap_len)
+               return 1;
 
-       if (to->start > from->start)
-               fromoff=to->start-from->start;
-       else
-               tooff=from->start-to->start;                    
-       size=to->len-tooff;
-       if (size > from->len-fromoff)
-               size=from->len-fromoff;
-       if (size < 0)
+       palette[regno].red   = red;
+       palette[regno].green = green;
+       palette[regno].blue  = blue;
+
+       switch (video_bpp) {
+#ifdef FBCON_HAS_MFB
+       case 1:
+               /* We shouldn't get here */
+               break;
+#endif
+#ifdef FBCON_HAS_CFB2
+       case 2:
+               if (macfb_setpalette)
+                       macfb_setpalette(regno, red, green, blue);
+               else
+                       return 1;
+               break;
+#endif
+#ifdef FBCON_HAS_CFB4
+       case 4:
+               if (macfb_setpalette)
+                       macfb_setpalette(regno, red, green, blue);
+               else
+                       return 1;
+               break;
+#endif
+#ifdef FBCON_HAS_CFB8
+       case 8:
+               if (macfb_setpalette)
+                       macfb_setpalette(regno, red, green, blue);
+               else
+                       return 1;
+               break;
+#endif
+#ifdef FBCON_HAS_CFB16
+       case 15:
+       case 16:
+               /* 1:5:5:5 */
+               fbcon_cmap.cfb16[regno] =
+                       ((red   & 0xf800) >>  1) |
+                       ((green & 0xf800) >>  6) |
+                       ((blue  & 0xf800) >> 11) |
+                       ((transp != 0) << 15);
+               break;
+#endif
+               /* I'm pretty sure that one or the other of these
+                  doesn't exist on 68k Macs */
+#ifdef FBCON_HAS_CFB24
+       case 24:
+               red   >>= 8;
+               green >>= 8;
+               blue  >>= 8;
+               fbcon_cmap.cfb24[regno] =
+                       (red   << macfb_defined.red.offset)   |
+                       (green << macfb_defined.green.offset) |
+                       (blue  << macfb_defined.blue.offset);
+               break;
+#endif
+#ifdef FBCON_HAS_CFB32
+       case 32:
+               red   >>= 8;
+               green >>= 8;
+               blue  >>= 8;
+               fbcon_cmap.cfb32[regno] =
+                       (red   << macfb_defined.red.offset)   |
+                       (green << macfb_defined.green.offset) |
+                       (blue  << macfb_defined.blue.offset);
+               break;
+#endif
+    }
+    return 0;
+}
+
+static void do_install_cmap(int con, struct fb_info *info)
+{
+       if (con != currcon)
                return;
-       size*=sizeof(unsigned short);
-       memcpy_fs(fsfromto, to->red+tooff, from->red+fromoff, size);
-       memcpy_fs(fsfromto, to->green+tooff, from->green+fromoff, size);
-       memcpy_fs(fsfromto, to->blue+tooff, from->blue+fromoff, size);
-       if (from->transp && to->transp)
-               memcpy_fs(fsfromto, to->transp+tooff, from->transp+fromoff, size);
+       if (fb_display[con].cmap.len)
+               fb_set_cmap(&fb_display[con].cmap, 1, macfb_setcolreg, info);
+       else
+               fb_set_cmap(fb_default_cmap(video_cmap_len), 1,
+                           macfb_setcolreg, info);
 }
+
 static int macfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
                          struct fb_info *info)
 {
-#if 0
-       printk("macfb_get_cmap: not supported!\n");
-       /* interferes with X11 */
-       if (console_loglevel < 7)
-               return -EINVAL;
        if (con == currcon) /* current console? */
-               return fb_get_cmap(cmap, kspc, 0 /*offb_getcolreg*/, info);
+               return fb_get_cmap(cmap, kspc, macfb_getcolreg, info);
        else if (fb_display[con].cmap.len) /* non default colormap? */
                fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
        else
-               fb_copy_cmap(fb_default_cmap(fb_display[con].var.bits_per_pixel),
+               fb_copy_cmap(fb_default_cmap(video_cmap_len),
                     cmap, kspc ? 0 : 2);
-#endif
-       copy_cmap(mac_get_default_cmap(fb_display[con].var.bits_per_pixel),
-            cmap, kspc ? 0 : 2);
        return 0;
-
 }
 
 static int macfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
                          struct fb_info *info)
 {
-#if 0
-       printk("macfb_set_cmap: not supported!\n");
-       if (console_loglevel < 7)
-               return -EINVAL;
+       int err;
+
        if (!fb_display[con].cmap.len) {        /* no colormap allocated? */
-               if ((err = fb_alloc_cmap(&fb_display[con].cmap,
-                                1<<fb_display[con].var.bits_per_pixel, 0)))
-               return err;
+               err = fb_alloc_cmap(&fb_display[con].cmap,video_cmap_len,0);
+               if (err)
+                       return err;
        }
        if (con == currcon)                     /* current console? */
-               return fb_set_cmap(cmap, kspc, 1 /*offb_setcolreg*/, info);
+               return fb_set_cmap(cmap, kspc, macfb_setcolreg, info);
        else
                fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
-#endif
        return 0;
 }
 
-static int macfb_pan_display(struct fb_var_screeninfo *var, int con,
-                            struct fb_info *info)
+static int macfb_ioctl(struct inode *inode, struct file *file,
+                      unsigned int cmd, unsigned long arg, int con,
+                      struct fb_info *info)
 {
-       /* no panning */
-       printk("macfb_pan: not supported!\n");
        return -EINVAL;
 }
 
-static int macfb_ioctl(struct inode *inode, struct file *file, 
-                      unsigned int cmd, unsigned long arg, int con,
-                      struct fb_info *info)
+static int macfb_pan_display(struct fb_var_screeninfo *var, int con,
+                            struct fb_info *info)
 {
-       printk("macfb_ioctl: not supported!\n");
        return -EINVAL;
 }
 
@@ -456,137 +871,405 @@ static struct fb_ops macfb_ops = {
        macfb_get_cmap,
        macfb_set_cmap,
        macfb_pan_display,
-       macfb_ioctl
+       macfb_ioctl,
+       NULL
 };
 
-int __init macfb_setup(char *options)
+void macfb_setup(char *options, int *ints)
 {
-    char *this_opt;
-
-    fb_info.fontname[0] = '\0';
-
-    if (!options || !*options)
-               return 0;
-     
-    for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {
-       if (!*this_opt) continue;
-
-       if (! strcmp(this_opt, "inverse"))
-               inverse=1;
-       else if (!strncmp(this_opt, "font:", 5)) {
-          strcpy(fb_info.fontname, this_opt+5);
-          printk("macfb_setup: option %s\n", this_opt);
+       char *this_opt;
+       
+       fb_info.fontname[0] = '\0';
+       
+       if (!options || !*options)
+               return;
+       
+       for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {
+               if (!*this_opt) continue;
+               
+               if (! strcmp(this_opt, "inverse"))
+                       inverse=1;
+               else if (!strncmp(this_opt, "font:", 5))
+                       strcpy(fb_info.fontname, this_opt+5);
+               /* This means "turn on experimental CLUT code" */
+               else if (!strcmp(this_opt, "vidtest"))
+                       vidtest=1;
        }
-    }
-    return 0;
 }
 
 static int macfb_switch(int con, struct fb_info *info)
 {
-       do_fb_set_var(&fb_display[con].var,1);
-       currcon=con;
-       return 0;
+       /* Do we have to save the colormap? */
+       if (fb_display[currcon].cmap.len)
+               fb_get_cmap(&fb_display[currcon].cmap, 1, macfb_getcolreg,
+                           info);
+       
+       currcon = con;
+       /* Install new colormap */
+       do_install_cmap(con, info);
+       macfb_update_var(con, info);
+       return 1;
 }
 
-/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
-
 static void macfb_blank(int blank, struct fb_info *info)
 {
        /* Not supported */
 }
 
-/*
- *     Nubus call back. This will give us our board identity and also
- *     other useful info we need later
- */
-static int nubus_video_card(struct nubus_device_specifier *ns, int slot, struct nubus_type *nt)
+void __init macfb_init(void)
 {
-       if(nt->category==NUBUS_CAT_DISPLAY)
-               return 0;
-       /* Claim all video cards. We don't yet do driver specifics though. */
-       return -ENODEV;
-}
-
-static struct nubus_device_specifier nb_video={
-       nubus_video_card,
-       NULL
-};
-
-int __init macfb_init(void)
-{
-       /* nubus_remap the video .. */
+       struct nubus_dev* ndev = NULL;
+       int video_is_nubus = 0;
 
        if (!MACH_IS_MAC) 
-               return -ENXIO;
-
-       mac_xres=mac_bi_data.dimensions&0xFFFF;
-       mac_yres=(mac_bi_data.dimensions&0xFFFF0000)>>16;
-       mac_depth=mac_bi_data.videodepth;
-       mac_xbytes=mac_bi_data.videorow;
-       mac_vxres = (mac_xbytes/mac_depth)*8;
-       mac_videosize=mac_xbytes*mac_yres;
-       mac_videobase=mac_bi_data.videoaddr;
-
-       printk("macfb_init: xres %d yres %d bpp %d addr %lx size %ld \n",
-               mac_xres, mac_yres, mac_depth, mac_videobase, mac_videosize);
+               return;
 
+       /* There can only be one internal video controller anyway so
+          we're not too worried about this */
+       video_width      = mac_bi_data.dimensions & 0xFFFF;
+       video_height     = mac_bi_data.dimensions >> 16;
+       video_bpp        = mac_bi_data.videodepth;
+       video_linelength = mac_bi_data.videorow;
+       video_size       = video_linelength * video_height;
+       /* Note: physical address (since 2.1.127) */
+       video_base       = (void*) mac_bi_data.videoaddr;
+       /* This is actually redundant with the initial mappings.
+          However, there are some non-obvious aspects to the way
+          those mappings are set up, so this is in fact the safest
+          way to ensure that this driver will work on every possible
+          Mac */
+       video_vbase      = ioremap(mac_bi_data.videoaddr, video_size);
+       
+       printk("macfb: framebuffer at 0x%p, mapped to 0x%p, size %dk\n",
+              video_base, video_vbase, video_size/1024);
+       printk("macfb: mode is %dx%dx%d, linelength=%d\n",
+              video_width, video_height, video_bpp, video_linelength);
+       
        /*
         *      Fill in the available video resolution
         */
         
-        macfb_defined.xres=mac_xres;
-        macfb_defined.yres=mac_yres;
-        macfb_defined.xres_virtual=mac_vxres;
-        macfb_defined.yres_virtual=mac_yres;
-        macfb_defined.bits_per_pixel=mac_depth;
-        
-        
-       /*
-        *      Let there be consoles..
-        */
-       strcpy(fb_info.modename, "Macintosh Builtin ");
-       fb_info.changevar = NULL;
-       fb_info.node = -1;
-       fb_info.fbops = &macfb_ops;
-       fb_info.disp=&disp;
-       fb_info.switch_con=&macfb_switch;
-       fb_info.updatevar=&fb_update_var;
-       fb_info.blank=&macfb_blank;
-       fb_info.flags = FBINFO_FLAG_DEFAULT;
-       do_fb_set_var(&macfb_defined,1);
-
-       macfb_get_var(&disp.var, -1, &fb_info);
-       macfb_set_disp(-1);
-
-       /*
-        *      Fill in the 8 bit color table if required
-        */
-       if (mac_depth == 8)
-               mac_set_cmap256(&default_256_colors);
-               
-       /*
-        *      Register the nubus hook
+       macfb_defined.xres           = video_width;
+       macfb_defined.yres           = video_height;
+       macfb_defined.xres_virtual   = video_width;
+       macfb_defined.yres_virtual   = video_height;
+       macfb_defined.bits_per_pixel = video_bpp;
+       macfb_defined.height = PIXEL_TO_MM(macfb_defined.yres);
+       macfb_defined.width  = PIXEL_TO_MM(macfb_defined.xres);  
+
+       printk("macfb: scrolling: redraw\n");
+       macfb_defined.yres_virtual = video_height;
+
+       /* some dummy values for timing to make fbset happy */
+       macfb_defined.pixclock     = 10000000 / video_width * 1000 / video_height;
+       macfb_defined.left_margin  = (video_width / 8) & 0xf8;
+       macfb_defined.right_margin = 32;
+       macfb_defined.upper_margin = 16;
+       macfb_defined.lower_margin = 4;
+       macfb_defined.hsync_len    = (video_width / 8) & 0xf8;
+       macfb_defined.vsync_len    = 4;
+
+       switch (video_bpp) {
+       case 1:
+               /* XXX: I think this will catch any program that tries
+                  to do FBIO_PUTCMAP when the visual is monochrome */
+               video_cmap_len = 0;
+               video_visual = FB_VISUAL_MONO01;
+               break;
+       case 2:
+       case 4:
+       case 8:
+               macfb_defined.red.length = video_bpp;
+               macfb_defined.green.length = video_bpp;
+               macfb_defined.blue.length = video_bpp;
+               video_cmap_len = 1 << video_bpp;
+               video_visual = FB_VISUAL_PSEUDOCOLOR;
+               break;
+       case 16:
+               macfb_defined.transp.offset = 15;
+               macfb_defined.transp.length = 1;
+               macfb_defined.red.offset = 10;
+               macfb_defined.red.length = 5;
+               macfb_defined.green.offset = 5;
+               macfb_defined.green.length = 5;
+               macfb_defined.blue.offset = 0;
+               macfb_defined.blue.length = 5;
+               printk("macfb: directcolor: "
+                      "size=1:5:5:5, shift=15:10:5:0\n");
+               video_cmap_len = 16;
+               /* Should actually be FB_VISUAL_DIRECTCOLOR, but this
+                  works too */
+               video_visual = FB_VISUAL_TRUECOLOR;
+               break;
+       case 24:
+       case 32:
+               /* XXX: have to test these... can any 68k Macs
+                  actually do this on internal video? */
+               macfb_defined.red.offset = 16;
+               macfb_defined.red.length = 8;
+               macfb_defined.green.offset = 8;
+               macfb_defined.green.length = 8;
+               macfb_defined.blue.offset = 0;
+               macfb_defined.blue.length = 8;
+               printk("macfb: truecolor: "
+                      "size=0:8:8:8, shift=0:16:8:0\n");
+               video_cmap_len = 16;
+               video_visual = FB_VISUAL_TRUECOLOR;
+       default:
+               video_cmap_len = 0;
+               video_visual = FB_VISUAL_MONO01;
+               printk("macfb: unknown or unsupported bit depth: %d\n", video_bpp);
+               break;
+       }
+       
+       /* Hardware dependent stuff */
+       /*  We take a wild guess that if the video physical address is
+        *  in nubus slot space, that the nubus card is driving video.
+        *  Penguin really ought to tell us whether we are using internal
+        *  video or not.
         */
-        
-       register_nubus_device(&nb_video);
+       /* Hopefully we only find one of them.  Otherwise our NuBus
+           code is really broken :-) */
 
-       if (register_framebuffer(&fb_info) < 0)
+       while ((ndev = nubus_find_type(NUBUS_CAT_DISPLAY, NUBUS_TYPE_VIDEO, ndev))
+               != NULL)
        {
-               return -EINVAL;
+               if (!(mac_bi_data.videoaddr >= ndev->board->slot_addr
+                     && (mac_bi_data.videoaddr <
+                         (unsigned long)nubus_slot_addr(ndev->board->slot+1))))
+                       continue;
+               video_is_nubus = 1;
+               /* We should probably just use the slot address... */
+               video_slot = ndev->board->slot;
+
+               switch(ndev->dr_hw) {
+               case NUBUS_DRHW_APPLE_MDC:
+                       strcpy( fb_info.modename, "Macintosh Display Card" );
+                       macfb_setpalette = mdc_setpalette;
+                       macfb_defined.activate = FB_ACTIVATE_NOW;
+                       break;
+               case NUBUS_DRHW_APPLE_TFB:
+                       strcpy( fb_info.modename, "Toby" );
+                       macfb_setpalette = toby_setpalette;
+                       macfb_defined.activate = FB_ACTIVATE_NOW;
+                       break;
+               case NUBUS_DRHW_APPLE_JET:
+                       strcpy(fb_info.modename, "Jet");
+                       macfb_setpalette = jet_setpalette;
+                       macfb_defined.activate = FB_ACTIVATE_NOW;
+                       break;                  
+               default:
+                       strcpy( fb_info.modename, "Generic NuBus" );
+                       break;
+               }
        }
 
-       printk("fb%d: %s frame buffer device using %ldK of video memory\n",
-              GET_FB_IDX(fb_info.node), fb_info.modename, mac_videosize>>10);
-       return 0;
-}
+       /* If it's not a NuBus card, it must be internal video */
+       /* FIXME: this function is getting way too big.  (this driver
+           is too...) */
+       if (!video_is_nubus)
+               switch( mac_bi_data.id )
+               {
+                       /* These don't have onboard video.  Eventually, we may
+                          be able to write separate framebuffer drivers for
+                          them (tobyfb.c, hiresfb.c, etc, etc) */
+               case MAC_MODEL_II:
+               case MAC_MODEL_IIX:
+               case MAC_MODEL_IICX:
+               case MAC_MODEL_IIFX:
+                       strcpy( fb_info.modename, "Generic NuBus" );
+                       break;
+
+                       /* Valkyrie Quadras */
+               case MAC_MODEL_Q630:
+                       /* I'm not sure about this one */
+               case MAC_MODEL_P588:
+                       strcpy( fb_info.modename, "Valkyrie built-in" );
+                       macfb_setpalette = valkyrie_setpalette;
+                       macfb_defined.activate = FB_ACTIVATE_NOW;
+                       valkyrie_cmap_regs = ioremap(DAC_BASE, 0x1000);
+                       break;
+
+                       /* DAFB Quadras */
+                       /* Note: these first four have the v7 DAFB, which is
+                          known to be rather unlike the ones used in the
+                          other models */
+               case MAC_MODEL_P475:
+               case MAC_MODEL_P475F:
+               case MAC_MODEL_P575:
+               case MAC_MODEL_Q605:
+       
+               case MAC_MODEL_Q800:
+               case MAC_MODEL_Q650:
+               case MAC_MODEL_Q610:
+               case MAC_MODEL_C650:
+               case MAC_MODEL_C610:
+               case MAC_MODEL_Q700:
+               case MAC_MODEL_Q900:
+               case MAC_MODEL_Q950:
+                       strcpy( fb_info.modename, "DAFB built-in" );
+                       macfb_setpalette = dafb_setpalette;
+                       macfb_defined.activate = FB_ACTIVATE_NOW;
+                       dafb_cmap_regs = ioremap(DAFB_BASE, 0x1000);
+                       break;
+
+                       /* LC II uses the V8 framebuffer */
+               case MAC_MODEL_LCII:
+                       strcpy( fb_info.modename, "V8 built-in" );
+                       macfb_setpalette = v8_brazil_setpalette;
+                       macfb_defined.activate = FB_ACTIVATE_NOW;
+                       v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000);
+                       break;
+               
+                       /* IIvi, IIvx use the "Brazil" framebuffer (which is
+                          very much like the V8, it seems, and probably uses
+                          the same DAC) */
+               case MAC_MODEL_IIVI:
+               case MAC_MODEL_IIVX:
+               case MAC_MODEL_P600:
+                       strcpy( fb_info.modename, "Brazil built-in" );
+                       macfb_setpalette = v8_brazil_setpalette;
+                       macfb_defined.activate = FB_ACTIVATE_NOW;
+                       v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000);
+                       break;
+               
+                       /* LC III (and friends) use the Sonora framebuffer */
+                       /* Incidentally this is also used in the non-AV models
+                          of the x100 PowerMacs */
+                       /* These do in fact seem to use the same DAC interface
+                          as the LC II. */
+               case MAC_MODEL_LCIII:
+               case MAC_MODEL_P520:
+               case MAC_MODEL_P550:
+               case MAC_MODEL_P460:
+                       macfb_setpalette = v8_brazil_setpalette;
+                       macfb_defined.activate = FB_ACTIVATE_NOW;
+                       strcpy( fb_info.modename, "Sonora built-in" );
+                       v8_brazil_cmap_regs = ioremap(DAC_BASE, 0x1000);
+                       break;
+
+                       /* IIci and IIsi use the infamous RBV chip
+                           (the IIsi is just a rebadged and crippled
+                           IIci in a different case, BTW) */
+               case MAC_MODEL_IICI:
+               case MAC_MODEL_IISI:
+                       macfb_setpalette = rbv_setpalette;
+                       macfb_defined.activate = FB_ACTIVATE_NOW;
+                       strcpy( fb_info.modename, "RBV built-in" );
+                       rbv_cmap_regs = ioremap(DAC_BASE, 0x1000);
+                       break;
+
+                       /* AVs use the Civic framebuffer */
+               case MAC_MODEL_Q840:
+               case MAC_MODEL_C660:
+                       macfb_setpalette = civic_setpalette;
+                       macfb_defined.activate = FB_ACTIVATE_NOW;
+                       strcpy( fb_info.modename, "Civic built-in" );
+                       civic_cmap_regs = ioremap(CIVIC_BASE, 0x1000);
+                       break;
+
+               
+                       /* Write a setpalette function for your machine, then
+                          you can add something similar here.  These are
+                          grouped by classes of video chipsets.  Some of this
+                          information is from the VideoToolbox "Bugs" web
+                          page at
+                          http://rajsky.psych.nyu.edu/Tips/VideoBugs.html */
+
+                       /* Assorted weirdos */
+                       /* We think this may be like the LC II */
+               case MAC_MODEL_LC:
+                       if (vidtest) {
+                               macfb_setpalette = v8_brazil_setpalette;
+                               macfb_defined.activate = FB_ACTIVATE_NOW;
+                               v8_brazil_cmap_regs =
+                                       ioremap(DAC_BASE, 0x1000);
+                       }
+                       strcpy( fb_info.modename, "LC built-in" );
+                       break;
+                       /* We think this may be like the LC II */
+               case MAC_MODEL_CCL:
+                       if (vidtest) {
+                               macfb_setpalette = v8_brazil_setpalette;
+                               macfb_defined.activate = FB_ACTIVATE_NOW;
+                               v8_brazil_cmap_regs =
+                                       ioremap(DAC_BASE, 0x1000);
+                       }
+                       strcpy( fb_info.modename, "Color Classic built-in" );
+                       break;
+
+                       /* And we *do* mean "weirdos" */
+               case MAC_MODEL_TV:
+                       strcpy( fb_info.modename, "Mac TV built-in" );
+                       break;
+
+                       /* These don't have colour, so no need to worry */
+               case MAC_MODEL_SE30:
+               case MAC_MODEL_CLII:
+                       strcpy( fb_info.modename, "Monochrome built-in" );
+                       break;
+
+                       /* Powerbooks are particularly difficult.  Many of
+                          them have separate framebuffers for external and
+                          internal video, which is admittedly pretty cool,
+                          but will be a bit of a headache to support here.
+                          Also, many of them are grayscale, and we don't
+                          really support that. */
+
+               case MAC_MODEL_PB140:
+               case MAC_MODEL_PB145:
+               case MAC_MODEL_PB170:
+                       strcpy( fb_info.modename, "DDC built-in" );
+                       break;
+
+                       /* Internal is GSC, External (if present) is ViSC */
+               case MAC_MODEL_PB150:   /* no external video */
+               case MAC_MODEL_PB160:
+               case MAC_MODEL_PB165:
+               case MAC_MODEL_PB180:
+               case MAC_MODEL_PB210:
+               case MAC_MODEL_PB230:
+                       strcpy( fb_info.modename, "GSC built-in" );
+                       break;
+
+                       /* Internal is TIM, External is ViSC */
+               case MAC_MODEL_PB165C:
+               case MAC_MODEL_PB180C:
+                       strcpy( fb_info.modename, "TIM built-in" );
+                       break;
+
+                       /* Internal is CSC, External is Keystone+Ariel. */
+               case MAC_MODEL_PB190:   /* external video is optional */
+               case MAC_MODEL_PB520:
+               case MAC_MODEL_PB250:
+               case MAC_MODEL_PB270C:
+               case MAC_MODEL_PB280:
+               case MAC_MODEL_PB280C:
+                       macfb_setpalette = csc_setpalette;
+                       macfb_defined.activate = FB_ACTIVATE_NOW;
+                       strcpy( fb_info.modename, "CSC built-in" );
+                       csc_cmap_regs = ioremap(CSC_BASE, 0x1000);
+                       break;
+               
+               default:
+                       strcpy( fb_info.modename, "Unknown/Unsupported built-in" );
+                       break;
+               }
+       
+       fb_info.changevar  = NULL;
+       fb_info.node       = -1;
+       fb_info.fbops      = &macfb_ops;
+       fb_info.disp       = &disp;
+       fb_info.switch_con = &macfb_switch;
+       fb_info.updatevar  = &macfb_update_var;
+       fb_info.blank      = &macfb_blank;
+       fb_info.flags      = FBINFO_FLAG_DEFAULT;
+       macfb_set_disp(-1);
+       do_install_cmap(0, &fb_info);
+       
+       if (register_framebuffer(&fb_info) < 0)
+               return;
 
-#if 0
-/*
- * These two auxiliary debug functions should go away ASAP. Only usage: 
- * before the console output is up (after head.S come some other crucial
- * setup routines :-) 
- *
- * Now in debug.c ...
- */
-#endif
+       printk("fb%d: %s frame buffer device\n",
+              GET_FB_IDX(fb_info.node), fb_info.modename);
+}
diff --git a/drivers/video/sun3fb.c b/drivers/video/sun3fb.c
new file mode 100644 (file)
index 0000000..5728c07
--- /dev/null
@@ -0,0 +1,729 @@
+/*
+ *  linux/drivers/video/sun3fb.c -- Frame buffer driver for Sun3
+ *
+ * (C) 1998 Thomas Bogendoerfer
+ *
+ * This driver is bases on sbusfb.c, which is
+ *
+ *     Copyright (C) 1998 Jakub Jelinek
+ *
+ *  This driver is partly based on the Open Firmware console driver
+ *
+ *     Copyright (C) 1997 Geert Uytterhoeven
+ *
+ *  and SPARC console subsystem
+ *
+ *      Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su)
+ *      Copyright (C) 1995-1997 David S. Miller (davem@caip.rutgers.edu)
+ *      Copyright (C) 1995-1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *      Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
+ *      Copyright (C) 1996-1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *      Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ *
+ *  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/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/selection.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/kd.h>
+#include <linux/vt_kern.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>       /* io_remap_page_range() */
+
+#ifdef CONFIG_SUN3
+#include <asm/oplib.h>
+#endif
+#ifdef CONFIG_SUN3X
+#include <asm/sun3x.h>
+#endif
+#include <video/sbusfb.h>
+
+#define DEFAULT_CURSOR_BLINK_RATE       (2*HZ/5)
+
+#define CURSOR_SHAPE                   1
+#define CURSOR_BLINK                   2
+
+    /*
+     *  Interface used by the world
+     */
+
+int sun3fb_init(void);
+int sun3fb_setup(char *options);
+
+static int currcon;
+static char fontname[40] __initdata = { 0 };
+static int curblink __initdata = 1;
+
+static int sun3fb_open(struct fb_info *info, int user);
+static int sun3fb_release(struct fb_info *info, int user);
+static int sun3fb_get_fix(struct fb_fix_screeninfo *fix, int con,
+                       struct fb_info *info);
+static int sun3fb_get_var(struct fb_var_screeninfo *var, int con,
+                       struct fb_info *info);
+static int sun3fb_set_var(struct fb_var_screeninfo *var, int con,
+                       struct fb_info *info);
+static int sun3fb_pan_display(struct fb_var_screeninfo *var, int con,
+                       struct fb_info *info);
+static int sun3fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+                       struct fb_info *info);
+static int sun3fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+                       struct fb_info *info);
+static int sun3fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+                           u_long arg, int con, struct fb_info *info);
+static void sun3fb_cursor(struct display *p, int mode, int x, int y);
+static void sun3fb_clear_margin(struct display *p, int s);
+                           
+
+    /*
+     *  Interface to the low level console driver
+     */
+
+static int sun3fbcon_switch(int con, struct fb_info *info);
+static int sun3fbcon_updatevar(int con, struct fb_info *info);
+static void sun3fbcon_blank(int blank, struct fb_info *info);
+
+
+    /*
+     *  Internal routines
+     */
+
+static int sun3fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+                           u_int *transp, struct fb_info *info);
+static int sun3fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                           u_int transp, struct fb_info *info);
+static void do_install_cmap(int con, struct fb_info *info);
+
+static struct fb_ops sun3fb_ops = {
+       sun3fb_open, sun3fb_release, sun3fb_get_fix, sun3fb_get_var, sun3fb_set_var,
+       sun3fb_get_cmap, sun3fb_set_cmap, sun3fb_pan_display, sun3fb_ioctl
+};
+
+
+    /*
+     *  Open/Release the frame buffer device
+     */
+
+static int sun3fb_open(struct fb_info *info, int user)
+{
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+static int sun3fb_release(struct fb_info *info, int user)
+{
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+static void sun3fb_clear_margin(struct display *p, int s)
+{
+       struct fb_info_sbusfb *fb = sbusfbinfod(p);
+
+       if (fb->switch_from_graph)
+               (*fb->switch_from_graph)(fb);
+       if (fb->fill) {
+               unsigned short rects [16];
+
+               rects [0] = 0;
+               rects [1] = 0;
+               rects [2] = fb->var.xres_virtual;
+               rects [3] = fb->y_margin;
+               rects [4] = 0;
+               rects [5] = fb->y_margin;
+               rects [6] = fb->x_margin;
+               rects [7] = fb->var.yres_virtual;
+               rects [8] = fb->var.xres_virtual - fb->x_margin;
+               rects [9] = fb->y_margin;
+               rects [10] = fb->var.xres_virtual;
+               rects [11] = fb->var.yres_virtual;
+               rects [12] = fb->x_margin;
+               rects [13] = fb->var.yres_virtual - fb->y_margin;
+               rects [14] = fb->var.xres_virtual - fb->x_margin;
+               rects [15] = fb->var.yres_virtual;
+               (*fb->fill)(fb, p, s, 4, rects);
+       } else {
+               unsigned char *fb_base = p->screen_base, *q;
+               int skip_bytes = fb->y_margin * fb->var.xres_virtual;
+               int scr_size = fb->var.xres_virtual * fb->var.yres_virtual;
+               int h, he, incr, size;
+
+               he = fb->var.yres;
+               if (fb->var.bits_per_pixel == 1) {
+                       fb_base -= (skip_bytes + fb->x_margin) / 8;
+                       skip_bytes /= 8;
+                       scr_size /= 8;
+                       mymemset (fb_base, skip_bytes - fb->x_margin / 8);
+                       mymemset (fb_base + scr_size - skip_bytes + fb->x_margin / 8, skip_bytes - fb->x_margin / 8);
+                       incr = fb->var.xres_virtual / 8;
+                       size = fb->x_margin / 8 * 2;
+                       for (q = fb_base + skip_bytes - fb->x_margin / 8, h = 0;
+                            h <= he; q += incr, h++)
+                               mymemset (q, size);
+               } else {
+                       fb_base -= (skip_bytes + fb->x_margin);
+                       memset (fb_base, attr_bgcol(p,s), skip_bytes - fb->x_margin);
+                       memset (fb_base + scr_size - skip_bytes + fb->x_margin, attr_bgcol(p,s), skip_bytes - fb->x_margin);
+                       incr = fb->var.xres_virtual;
+                       size = fb->x_margin * 2;
+                       for (q = fb_base + skip_bytes - fb->x_margin, h = 0;
+                            h <= he; q += incr, h++)
+                               memset (q, attr_bgcol(p,s), size);
+               }
+       }
+}
+
+static void sun3fb_disp_setup(struct display *p)
+{
+       struct fb_info_sbusfb *fb = sbusfbinfod(p);
+
+       if (fb->setup)
+               fb->setup(p);   
+       sun3fb_clear_margin(p, 0);
+}
+
+    /*
+     *  Get the Fixed Part of the Display
+     */
+
+static int sun3fb_get_fix(struct fb_fix_screeninfo *fix, int con,
+                         struct fb_info *info)
+{
+       struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+       memcpy(fix, &fb->fix, sizeof(struct fb_fix_screeninfo));
+       return 0;
+}
+
+    /*
+     *  Get the User Defined Part of the Display
+     */
+
+static int sun3fb_get_var(struct fb_var_screeninfo *var, int con,
+                         struct fb_info *info)
+{
+       struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+       memcpy(var, &fb->var, sizeof(struct fb_var_screeninfo));
+       return 0;
+}
+
+    /*
+     *  Set the User Defined Part of the Display
+     */
+
+static int sun3fb_set_var(struct fb_var_screeninfo *var, int con,
+                       struct fb_info *info)
+{
+       struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+       if (var->xres > fb->var.xres || var->yres > fb->var.yres ||
+           var->xres_virtual > fb->var.xres_virtual ||
+           var->yres_virtual > fb->var.yres_virtual ||
+           var->bits_per_pixel != fb->var.bits_per_pixel ||
+           var->nonstd ||
+           (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
+               return -EINVAL;
+       memcpy(var, &fb->var, sizeof(struct fb_var_screeninfo));
+       return 0;
+}
+
+    /*
+     *  Pan or Wrap the Display
+     *
+     *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+     */
+
+static int sun3fb_pan_display(struct fb_var_screeninfo *var, int con,
+                             struct fb_info *info)
+{
+       if (var->xoffset || var->yoffset)
+               return -EINVAL;
+       else
+               return 0;
+}
+
+    /*
+     *  Hardware cursor
+     */
+     
+static unsigned char hw_cursor_cmap[2] = { 0, 0xff };
+
+static void
+sun3fb_cursor_timer_handler(unsigned long dev_addr)
+{
+       struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)dev_addr;
+        
+       if (!fb->setcursor) return;
+                                
+       if (fb->cursor.mode & CURSOR_BLINK) {
+               fb->cursor.enable ^= 1;
+               fb->setcursor(fb);
+       }
+       
+       fb->cursor.timer.expires = jiffies + fb->cursor.blink_rate;
+       add_timer(&fb->cursor.timer);
+}
+
+static void sun3fb_cursor(struct display *p, int mode, int x, int y)
+{
+       struct fb_info_sbusfb *fb = sbusfbinfod(p);
+       
+       switch (mode) {
+       case CM_ERASE:
+               fb->cursor.mode &= ~CURSOR_BLINK;
+               fb->cursor.enable = 0;
+               (*fb->setcursor)(fb);
+               break;
+                                 
+       case CM_MOVE:
+       case CM_DRAW:
+               if (fb->cursor.mode & CURSOR_SHAPE) {
+                       fb->cursor.size.fbx = fontwidth(p);
+                       fb->cursor.size.fby = fontheight(p);
+                       fb->cursor.chot.fbx = 0;
+                       fb->cursor.chot.fby = 0;
+                       fb->cursor.enable = 1;
+                       memset (fb->cursor.bits, 0, sizeof (fb->cursor.bits));
+                       fb->cursor.bits[0][fontheight(p) - 2] = (0xffffffff << (32 - fontwidth(p)));
+                       fb->cursor.bits[1][fontheight(p) - 2] = (0xffffffff << (32 - fontwidth(p)));
+                       fb->cursor.bits[0][fontheight(p) - 1] = (0xffffffff << (32 - fontwidth(p)));
+                       fb->cursor.bits[1][fontheight(p) - 1] = (0xffffffff << (32 - fontwidth(p)));
+                       (*fb->setcursormap) (fb, hw_cursor_cmap, hw_cursor_cmap, hw_cursor_cmap);
+                       (*fb->setcurshape) (fb);
+               }
+               fb->cursor.mode = CURSOR_BLINK;
+               if (fontwidthlog(p))
+                       fb->cursor.cpos.fbx = (x << fontwidthlog(p)) + fb->x_margin;
+               else
+                       fb->cursor.cpos.fbx = (x * fontwidth(p)) + fb->x_margin;
+               if (fontheightlog(p))
+                       fb->cursor.cpos.fby = (y << fontheightlog(p)) + fb->y_margin;
+               else
+                       fb->cursor.cpos.fby = (y * fontheight(p)) + fb->y_margin;
+               (*fb->setcursor)(fb);
+               break;
+       }
+}
+
+    /*
+     *  Get the Colormap
+     */
+
+static int sun3fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+                        struct fb_info *info)
+{
+       if (con == currcon) /* current console? */
+               return fb_get_cmap(cmap, kspc, sun3fb_getcolreg, info);
+       else if (fb_display[con].cmap.len) /* non default colormap? */
+               fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+       else
+               fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2);
+       return 0;
+}
+
+    /*
+     *  Set the Colormap
+     */
+
+static int sun3fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+                        struct fb_info *info)
+{
+       int err;
+
+       if (!fb_display[con].cmap.len) {        /* no colormap allocated? */
+               if ((err = fb_alloc_cmap(&fb_display[con].cmap, 1<<fb_display[con].var.bits_per_pixel, 0)))
+                       return err;
+       }
+       if (con == currcon) {                   /* current console? */
+               err = fb_set_cmap(cmap, kspc, sun3fb_setcolreg, info);
+               if (!err) {
+                       struct fb_info_sbusfb *fb = sbusfbinfo(info);
+                       
+                       if (fb->loadcmap)
+                               (*fb->loadcmap)(fb, &fb_display[con], cmap->start, cmap->len);
+               }
+               return err;
+       } else
+               fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+       return 0;
+}
+
+static int sun3fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+                       u_long arg, int con, struct fb_info *info)
+{
+       return -EINVAL;
+}
+
+    /*
+     *  Setup: parse used options
+     */
+
+__initfunc(void sun3fb_setup(char *options))
+{
+       char *p;
+       
+       for (p = options;;) {
+               if (!strncmp(p, "font=", 5)) {
+                       int i;
+                       
+                       for (i = 0; i < sizeof(fontname) - 1; i++)
+                               if (p[i+5] == ' ' || !p[i+5])
+                                       break;
+                       memcpy(fontname, p+5, i);
+                       fontname[i] = 0;
+               } else if (!strncmp(p, "noblink", 7))
+                       curblink = 0;
+               while (*p && *p != ' ' && *p != ',') p++;
+               if (*p != ',') break;
+               p++;
+       }
+
+       return 0;
+}
+
+static int sun3fbcon_switch(int con, struct fb_info *info)
+{
+       int x_margin, y_margin;
+       struct fb_info_sbusfb *fb = sbusfbinfo(info);
+       int lastconsole;
+    
+       /* Do we have to save the colormap? */
+       if (fb_display[currcon].cmap.len)
+               fb_get_cmap(&fb_display[currcon].cmap, 1, sun3fb_getcolreg, info);
+
+       if (info->display_fg) {
+               lastconsole = info->display_fg->vc_num;
+               if (lastconsole != con && 
+                   (fontwidth(&fb_display[lastconsole]) != fontwidth(&fb_display[con]) ||
+                    fontheight(&fb_display[lastconsole]) != fontheight(&fb_display[con])))
+                       fb->cursor.mode |= CURSOR_SHAPE;
+       }
+       x_margin = (fb_display[con].var.xres_virtual - fb_display[con].var.xres) / 2;
+       y_margin = (fb_display[con].var.yres_virtual - fb_display[con].var.yres) / 2;
+       if (fb->margins)
+               fb->margins(fb, &fb_display[con], x_margin, y_margin);
+       if (fb->graphmode || fb->x_margin != x_margin || fb->y_margin != y_margin) {
+               fb->x_margin = x_margin; fb->y_margin = y_margin;
+               sun3fb_clear_margin(&fb_display[con], 0);
+       }
+       currcon = con;
+       /* Install new colormap */
+       do_install_cmap(con, info);
+       return 0;
+}
+
+    /*
+     *  Update the `var' structure (called by fbcon.c)
+     */
+
+static int sun3fbcon_updatevar(int con, struct fb_info *info)
+{
+       /* Nothing */
+       return 0;
+}
+
+    /*
+     *  Blank the display.
+     */
+
+static void sun3fbcon_blank(int blank, struct fb_info *info)
+{
+    struct fb_info_sbusfb *fb = sbusfbinfo(info);
+    
+    if (blank && fb->blank)
+       return fb->blank(fb);
+    else if (!blank && fb->unblank)
+       return fb->unblank(fb);
+}
+
+    /*
+     *  Read a single color register and split it into
+     *  colors/transparent. Return != 0 for invalid regno.
+     */
+
+static int sun3fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+                         u_int *transp, struct fb_info *info)
+{
+       struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+       if (!fb->color_map || regno > 255)
+               return 1;
+       *red = (fb->color_map CM(regno, 0)<<8) | fb->color_map CM(regno, 0);
+       *green = (fb->color_map CM(regno, 1)<<8) | fb->color_map CM(regno, 1);
+       *blue = (fb->color_map CM(regno, 2)<<8) | fb->color_map CM(regno, 2);
+       *transp = 0;
+       return 0;
+}
+
+
+    /*
+     *  Set a single color register. The values supplied are already
+     *  rounded down to the hardware's capabilities (according to the
+     *  entries in the var structure). Return != 0 for invalid regno.
+     */
+
+static int sun3fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                           u_int transp, struct fb_info *info)
+{
+       struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+       if (!fb->color_map || regno > 255)
+               return 1;
+       red >>= 8;
+       green >>= 8;
+       blue >>= 8;
+       fb->color_map CM(regno, 0) = red;
+       fb->color_map CM(regno, 1) = green;
+       fb->color_map CM(regno, 2) = blue;
+       return 0;
+}
+
+
+static void do_install_cmap(int con, struct fb_info *info)
+{
+       struct fb_info_sbusfb *fb = sbusfbinfo(info);
+       
+       if (con != currcon)
+               return;
+       if (fb_display[con].cmap.len)
+               fb_set_cmap(&fb_display[con].cmap, 1, sun3fb_setcolreg, info);
+       else
+               fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+                           1, sun3fb_setcolreg, info);
+       if (fb->loadcmap)
+               (*fb->loadcmap)(fb, &fb_display[con], 0, 256);
+}
+
+static int sun3fb_set_font(struct display *p, int width, int height)
+{
+       int w = p->var.xres_virtual, h = p->var.yres_virtual;
+       int depth = p->var.bits_per_pixel;
+       struct fb_info_sbusfb *fb = sbusfbinfod(p);
+       int x_margin, y_margin;
+       
+       if (depth > 8) depth = 8;
+       x_margin = (w % width) / 2;
+       y_margin = (h % height) / 2;
+
+       p->var.xres = w - 2*x_margin;
+       p->var.yres = h - 2*y_margin;
+       
+       fb->cursor.mode |= CURSOR_SHAPE;
+       
+       if (fb->margins)
+               fb->margins(fb, p, x_margin, y_margin);
+       if (fb->x_margin != x_margin || fb->y_margin != y_margin) {
+               fb->x_margin = x_margin; fb->y_margin = y_margin;
+               sun3fb_clear_margin(p, 0);
+       }
+
+       return 1;
+}
+
+void sun3fb_palette(int enter)
+{
+       int i;
+       struct display *p;
+       
+       for (i = 0; i < MAX_NR_CONSOLES; i++) {
+               p = &fb_display[i];
+               if (p->dispsw && p->dispsw->setup == sun3fb_disp_setup &&
+                   p->fb_info->display_fg &&
+                   p->fb_info->display_fg->vc_num == i) {
+                       struct fb_info_sbusfb *fb = sbusfbinfod(p);
+
+                       if (fb->restore_palette) {
+                               if (enter)
+                                       fb->restore_palette(fb);
+                               else if (vt_cons[i]->vc_mode != KD_GRAPHICS)
+                                        vc_cons[i].d->vc_sw->con_set_palette(vc_cons[i].d, color_table);
+                       }
+               }
+       }
+}
+
+    /*
+     *  Initialisation
+     */
+__initfunc(static void sun3fb_init_fb(int fbtype, unsigned long addr))
+{
+       static struct linux_sbus_device sdb;
+       struct fb_fix_screeninfo *fix;
+       struct fb_var_screeninfo *var;
+       struct display *disp;
+       struct fb_info_sbusfb *fb;
+       struct fbtype *type;
+       int linebytes, w, h, depth;
+       char *p = NULL;
+
+       fb = kmalloc(sizeof(struct fb_info_sbusfb), GFP_ATOMIC);
+       if (!fb)
+               return -ENOMEM;
+       
+       memset(fb, 0, sizeof(struct fb_info_sbusfb));
+       fix = &fb->fix;
+       var = &fb->var;
+       disp = &fb->disp;
+       type = &fb->type;
+       
+       sdb.reg_addrs[0].phys_addr = addr;
+       fb->sbdp = &sdb;
+
+       type->fb_type = fbtype;
+       
+       type->fb_height = h = 900;
+       type->fb_width  = w = 1152;
+sizechange:
+       type->fb_depth  = depth = (fbtype == FBTYPE_SUN2BW) ? 1 : 8;
+       linebytes = w * depth / 8;
+       type->fb_size   = PAGE_ALIGN((linebytes) * h);
+       
+       fb->x_margin = (w & 7) / 2;
+       fb->y_margin = (h & 15) / 2;
+
+       var->xres_virtual = w;
+       var->yres_virtual = h;
+       var->xres = w - 2*fb->x_margin;
+       var->yres = h - 2*fb->y_margin;
+       
+       var->bits_per_pixel = depth;
+       var->height = var->width = -1;
+       var->pixclock = 10000;
+       var->vmode = FB_VMODE_NONINTERLACED;
+       var->red.length = var->green.length = var->blue.length = 8;
+
+       fix->line_length = linebytes;
+       fix->smem_len = type->fb_size;
+       fix->type = FB_TYPE_PACKED_PIXELS;
+       fix->visual = FB_VISUAL_PSEUDOCOLOR;
+       
+       fb->info.node = -1;
+       fb->info.fbops = &sun3fb_ops;
+       fb->info.disp = disp;
+       strcpy(fb->info.fontname, fontname);
+       fb->info.changevar = NULL;
+       fb->info.switch_con = &sun3fbcon_switch;
+       fb->info.updatevar = &sun3fbcon_updatevar;
+       fb->info.blank = &sun3fbcon_blank;
+       fb->info.flags = FBINFO_FLAG_DEFAULT;
+       
+       fb->cursor.hwsize.fbx = 32;
+       fb->cursor.hwsize.fby = 32;
+       
+       if (depth > 1 && !fb->color_map)
+               fb->color_map = kmalloc(256 * 3, GFP_ATOMIC);
+               
+       switch(fbtype) {
+#ifdef CONFIG_FB_CGSIX
+       case FBTYPE_SUNFAST_COLOR:
+               p = cgsixfb_init(fb); break;
+#endif
+#ifdef CONFIG_FB_BWTWO
+       case FBTYPE_SUN2BW:
+               p = bwtwofb_init(fb); break;
+#endif
+       }
+       fix->smem_start = fb->disp.screen_base;
+       
+       if (!p) {
+               kfree(fb);
+               -ENODEV;
+       }
+       
+       if (p == SBUSFBINIT_SIZECHANGE)
+               goto sizechange;
+
+       disp->dispsw = &fb->dispsw;
+       if (fb->setcursor) {
+               fb->dispsw.cursor = sun3fb_cursor;
+               if (curblink) {
+                       fb->cursor.blink_rate = DEFAULT_CURSOR_BLINK_RATE;
+                       init_timer(&fb->cursor.timer);
+                       fb->cursor.timer.expires = jiffies + fb->cursor.blink_rate;
+                       fb->cursor.timer.data = (unsigned long)fb;
+                       fb->cursor.timer.function = sun3fb_cursor_timer_handler;
+                       add_timer(&fb->cursor.timer);
+               }
+       }
+       fb->cursor.mode = CURSOR_SHAPE;
+       fb->dispsw.set_font = sun3fb_set_font;
+       fb->setup = fb->dispsw.setup;
+       fb->dispsw.setup = sun3fb_disp_setup;
+       fb->dispsw.clear_margins = NULL;
+
+       disp->var = *var;
+       disp->visual = fix->visual;
+       disp->type = fix->type;
+       disp->type_aux = fix->type_aux;
+       disp->line_length = fix->line_length;
+       
+       if (fb->blank)
+               disp->can_soft_blank = 1;
+
+       sun3fb_set_var(var, -1, &fb->info);
+
+       if (register_framebuffer(&fb->info) < 0) {
+               kfree(fb);
+               return -EINVAL;
+       }
+       printk("fb%d: %s\n", GET_FB_IDX(fb->info.node), p);
+
+       return 0;
+}
+
+
+__initfunc(int sun3fb_init(void))
+{
+       extern int con_is_present(void);
+       unsigned long addr;
+       char p4id;
+       
+       if (!con_is_present()) return;
+       printk("sun3fb_init()\n");
+#ifdef CONFIG_SUN3
+       addr = 0xfe20000;
+        switch(*(romvec->pv_fbtype))
+        {
+               case FBTYPE_SUN2BW:
+                       return sun3fb_init_fb(FBTYPE_SUN2BW, addr);
+               case FBTYPE_SUN3COLOR:
+                       printk("cg3 detected but not supported\n");
+                       return -EINVAL;
+       }
+#else
+       addr = SUN3X_VIDEO_BASE;
+       p4id = *(char *)SUN3X_VIDEO_P4ID;
+
+       p4id = (p4id == 0x45) ? p4id : (p4id & 0xf0);
+       switch (p4id) {
+               case 0x00:
+                       return sun3fb_init_fb(FBTYPE_SUN2BW, addr);
+#if 0 /* not yet */
+               case 0x40:
+                       sun3fb_init_fb(FBTYPE_SUN4COLOR, addr);
+                       break;
+               case 0x45:
+                       sun3fb_init_fb(FBTYPE_SUN8COLOR, addr);
+                       break;
+#endif
+               case 0x60:
+                       return sun3fb_init_fb(FBTYPE_SUNFAST_COLOR, addr);
+       }
+#endif                 
+}
index 7716dc422e4ee2efd4b394ce48b0dd433eba24c4..db9a60c35fa28932a7d0162b52c99ee9a520abb3 100644 (file)
@@ -26,6 +26,7 @@ extern pgd_t swapper_pg_dir[1024];
 #define flush_cache_page(vma, vmaddr)          do { } while (0)
 #define flush_page_to_ram(page)                        do { } while (0)
 #define flush_icache_range(start, end)         do { } while (0)
+#define flush_icache_page(vma,pg)              do { } while (0)
 
 #define __flush_tlb()                                                  \
        do {                                                            \
diff --git a/include/asm-m68k/adb_iop.h b/include/asm-m68k/adb_iop.h
new file mode 100644 (file)
index 0000000..8a48e56
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * ADB through the IOP
+ * Written by Joshua M. Thompson
+ */
+
+/* IOP number and channel number for ADB */
+
+#define ADB_IOP                IOP_NUM_ISM
+#define ADB_CHAN       2
+
+/* From the A/UX headers...maybe important, maybe not */
+
+#define ADB_IOP_LISTEN 0x01
+#define ADB_IOP_TALK   0x02
+#define ADB_IOP_EXISTS 0x04
+#define ADB_IOP_FLUSH  0x08
+#define ADB_IOP_RESET  0x10
+#define ADB_IOP_INT    0x20
+#define ADB_IOP_POLL   0x40
+#define ADB_IOP_UNINT  0x80
+
+#define AIF_RESET      0x00
+#define AIF_FLUSH      0x01
+#define AIF_LISTEN     0x08
+#define AIF_TALK       0x0C
+
+/* Flag bits in struct adb_iopmsg */
+
+#define ADB_IOP_EXPLICIT       0x80    /* nonzero if explicit command */
+#define ADB_IOP_AUTOPOLL       0x40    /* auto/SRQ polling enabled    */
+#define ADB_IOP_SRQ            0x04    /* SRQ detected                */
+#define ADB_IOP_TIMEOUT                0x02    /* nonzero if timeout          */
+
+#ifndef __ASSEMBLY__
+
+struct adb_iopmsg {
+       __u8 flags;             /* ADB flags         */
+       __u8 count;             /* no. of data bytes */
+       __u8 cmd;               /* ADB command       */
+       __u8 data[8];           /* ADB data          */
+       __u8 spare[21];         /* spare             */
+};
+
+#endif /* __ASSEMBLY__ */
index 0e3cd0c6f0e3e4b262b75648b995c7b5a8ce9437..aac7e8597962c955141f6542f0fcdb896d27a83b 100644 (file)
@@ -7,6 +7,8 @@ extern unsigned int local_irq_count[NR_CPUS];
 
 #define in_interrupt() (local_irq_count[smp_processor_id()] + local_bh_count[smp_processor_id()] != 0)
 
+#define in_irq() (local_irq_count[smp_processor_id()] != 0)
+
 #define hardirq_trylock(cpu)   (local_irq_count[cpu] == 0)
 #define hardirq_endlock(cpu)   do { } while (0)
 
index 0a62d30ef96a9f6e913c72aa2d393f5cc5cce604..be2dfbd61ea17d166dfc37981a6154b1a765add1 100644 (file)
@@ -15,7 +15,7 @@
  * Currently the Atari has 72 and the Amiga 24, but if both are
  * supported in the kernel it is better to make room for 72.
  */
-#if defined(CONFIG_ATARI)
+#if defined(CONFIG_ATARI) || defined(CONFIG_MAC)
 #define NR_IRQS (72+SYS_IRQS)
 #else
 #define NR_IRQS (24+SYS_IRQS)
diff --git a/include/asm-m68k/mac_baboon.h b/include/asm-m68k/mac_baboon.h
new file mode 100644 (file)
index 0000000..e878508
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Definitions for the "Baboon" custom IC on the PowerBook 190.
+ */
+
+#define BABOON_BASE (0x50F1A000)       /* same as IDE controller base */
+
+#ifndef __ASSEMBLY__
+
+struct baboon {
+       char    pad1[208];      /* generic IDE registers, not used here */
+       short   mb_control;     /* Control register:
+                                * bit 5 : slot 2 power control
+                                * bit 6 : slot 1 power control
+                                */
+       char    pad2[2];
+       short   mb_status;      /* (0xD4) media bay status register:
+                                *
+                                * bit 0: ????
+                                * bit 1: IDE interrupt active?
+                                * bit 2: bay status, 0 = full, 1 = empty
+                                * bit 3: ????
+                                */
+       char    pad3[2];        /* (0xD6) not used */
+       short   mb_ifr;         /* (0xD8) media bay interrupt flags register:
+                                *
+                                * bit 0: ????
+                                * bit 1: IDE controller interrupt
+                                * bit 2: media bay status change interrupt
+                                */
+};
+
+extern volatile struct baboon *baboon;
+
+#endif /* __ASSEMBLY **/
index 40905e74da78d0e0eb88e048d902835d90fe5bc2..e71462a8b838dc2ad313c5d8b0f9a06123791b54 100644 (file)
@@ -74,6 +74,7 @@ struct mac_model
 #define MAC_IDE_NONE           0
 #define MAC_IDE_QUADRA         1
 #define MAC_IDE_PB             2
+#define MAC_IDE_BABOON         3
 
 #define MAC_SCC_II             1
 #define MAC_SCC_QUADRA         2
index 1014df89d0c30056459d3cd557d39c2e3681d5e1..f60ffdc8b9937889fd1d85bc42ff1c946a62a378 100644 (file)
 #define PSC5_SOURCE_BASE       40
 #define PSC6_SOURCE_BASE       48
 #define NUBUS_SOURCE_BASE      56
+#define BABOON_SOURCE_BASE     64
 
 /*
- * Maximum IRQ number is NUBUS_SOURCE_BASE + 7,
- * giving us IRQs up through 63.
+ * Maximum IRQ number is BABOON_SOURCE_BASE + 7,
+ * giving us IRQs up through 71
  */
 
-#define NUM_MAC_SOURCES                64
+#define NUM_MAC_SOURCES                72
 
 /* 
  * clean way to separate IRQ into its source and index
 #define IRQ_NUBUS_E      (61)
 #define IRQ_NUBUS_F      (62)
 
+/* Baboon interrupts (cascaded to nubus slot $C) */
+#define IRQ_BABOON_0     (64)
+#define IRQ_BABOON_1     (65)
+#define IRQ_BABOON_2     (66)
+#define IRQ_BABOON_3     (67)
+
 #define SLOT2IRQ(x)      (x + 47)
 #define IRQ2SLOT(x)      (x - 47)
 
index f27b11256675aa8d6c447194d1c7e2c3d69125d4..a51563cd71f4e89021c24aca8be2d2f83c1920d8 100644 (file)
@@ -7,59 +7,12 @@
 
 #include <asm/atomic.h>
 
-#define get_active_bhs()       (bh_mask & bh_active)
-#define clear_active_bhs(x)    atomic_clear_mask((x),&bh_active)
-
-extern inline void init_bh(int nr, void (*routine)(void))
-{
-       bh_base[nr] = routine;
-       atomic_set(&bh_mask_count[nr], 0);
-       bh_mask |= 1 << nr;
-}
-
-extern inline void mark_bh(int nr)
-{
-       set_bit(nr, &bh_active);
-}
-
-/*
- * These use a mask count to correctly handle
- * nested disable/enable calls
- */
-extern inline void disable_bh(int nr)
-{
-       bh_mask &= ~(1 << nr);
-       atomic_inc(&bh_mask_count[nr]);
-}
-
-extern inline void enable_bh(int nr)
-{
-       if (atomic_dec_and_test(&bh_mask_count[nr]))
-               bh_mask |= 1 << nr;
-}
-
-extern inline void remove_bh(int nr)
-{
-       bh_base[nr] = NULL;
-       bh_mask &= ~(1 << nr);
-}
-
 extern unsigned int local_bh_count[NR_CPUS];
 
 #define local_bh_disable()     (local_bh_count[smp_processor_id()]++)
 #define local_bh_enable()      (local_bh_count[smp_processor_id()]--)
 
-extern inline void start_bh_atomic(void)
-{
-       local_bh_count[smp_processor_id()]++;
-       barrier();
-}
-
-extern inline void end_bh_atomic(void)
-{
-       barrier();
-       local_bh_count[smp_processor_id()]--;
-}
+#define in_softirq() (local_bh_count != 0)
 
 /* These are for the irq's testing the lock */
 #define softirq_trylock(cpu)  (local_bh_count[cpu] ? 0 : (local_bh_count[cpu]=1))