diff -urN pc-2.0.21/Documentation/Configure.help linux-2.0.21/Documentation/Configure.help
--- pc-2.0.21/Documentation/Configure.help	Thu Sep 12 07:27:33 1996
+++ linux-2.0.21/Documentation/Configure.help	Thu Sep 26 18:39:29 1996
@@ -3788,6 +3788,305 @@
   machine see http://cap.anu.edu.au/cap/projects/linux or mail to
   hackers@cafe.anu.edu.au
 
+# m68k-specific kernel options
+# Documented by Chris Lawrence <quango@socomm.net>
+
+Amiga support
+CONFIG_AMIGA
+  This option enables support for the Amiga computer.  If you plan to use
+  this kernel on an Amiga, say Y here; otherwise say N.
+
+Atari support
+CONFIG_ATARI
+  This option enables support for the Atari ST/TT/Medusa/Falcon.  If you
+  plan to use this kernel on an Atari, say Y here; otherwise say N.
+
+Macintosh support
+CONFIG_MAC
+  This option would enable support for the Apple Macintosh if there was
+  any for it.  Say N unless you've coded all the necessary support ;).
+
+68020 support
+CONFIG_M68020
+  If you anticipate running this kernel on a computer with a MC68020 processor,
+  say Y.  Otherwise, say N.  Note that the 68020 requires a 68851 MMU 
+  (= memory management unit) to run Linux/m68k.
+
+68030 support
+CONFIG_M68030
+  If you anticipate running this kernel on a computer with a MC68030 processor,
+  say Y.  Otherwise, say N.  Note that a MC68EC030 will not work, as it does
+  not include an MMU (= memory management unit).
+
+68040 support
+CONFIG_M68040
+  If you anticipate running this kernel on a computer with a MC68LC040 or
+  MC68040 processor, say Y.  Otherwise, say N.  Note that an MC68EC040 will
+  not work, as it does not include an MMU (= memory management unit).
+
+Use -m68040 flag for 68040 specific optimizations
+CONFIG_OPTIMIZE_040
+  If you will only be running this kernel on a 68040-series processor,
+  this will make the kernel run somewhat faster.  However, it will no longer
+  run on a 68020 or 68030, no matter whether you included 68020 and 68030
+  support or not.  Say N unless the only processor you are compiling support
+  for is the 68040.
+
+68060 support
+CONFIG_M68060
+  If you anticipate running this kernel on a computer with a MC68060
+  processor, say Y.  Otherwise, say N.
+
+Use -m68060 flag for 68060 specific optimizations
+CONFIG_OPTIMIZE_060
+  If you will only be running this kernel on a 68060-series processor,
+  this will make the kernel run somewhat faster.  However, it will no longer
+  run on a 68020, 68030 or 68040, no matter whether you included support
+  for those processors or not.  Say N unless the only processor you are
+  compiling support for is the 68060.
+
+Amiga AutoConfig Identification
+CONFIG_ZORRO
+  This enables support for automatic identification of Amiga expansion
+  cards that obey the AutoConfig(tm) specification.  You should say Y
+  to this question unless you have no expansion cards and no intention
+  of getting any.
+
+Amiga OCS chipset support
+CONFIG_AMIFB_OCS
+  This enables support for the original Agnus and Denise video chips,
+  found in the Amiga 1000 and most A500's and A2000's.  If you intend
+  to run Linux on any of these systems, say Y, otherwise say N.
+
+Amiga ECS chipset support
+CONFIG_AMIFB_ECS
+  This enables support for the Enhanced Chip Set, found in later A500's,
+  later A2000's, the A600, the A3000, the A3000T and CDTV.  If you intend to
+  run Linux on any of these systems, say Y, otherwise say N.
+
+Amiga AGA chipset support
+CONFIG_AMIFB_AGA
+  This enables support for the Advanced Graphics Architecture (aka AGA
+  or AA) Chip Set, found in the A1200, A4000, A4000T and CD32.  If you intend
+  to run Linux on any of these systems, say Y, otherwise say N.
+
+Amiga Cybervision support
+CONFIG_FB_CYBER
+  This enables support for the Cybervision 64 graphics card from Phase5.
+  Please note that its use is not all that intuitive (i.e. if you have
+  any questions, be sure to ask!).  Say N unless you have a Cybervision
+  64 or plan to get one before you next recompile the kernel.
+
+Amiga GSP (TMS340x0) support
+CONFIG_AMIGA_GSP
+  Include support for Amiga graphics cards that use the Texas Instruments
+  TMS340x0 graphics chips.  Say Y if you want to use a DMI Resolver or
+  Commodore A2410 (Lowell) graphics card on an Amiga; otherwise, say N.
+
+DMI Resolver support
+CONFIG_GSP_RESOLVER
+  Include support in the kernel for the DMI Resolver graphics card.  If
+  you have one, say Y; otherwise, say N.
+
+A2410 support
+CONFIG_GSP_A2410
+  Include support in the kernel for the Commodore/University of Lowell
+  A2410 graphics card.  If you have one, say Y; otherwise, say N.
+
+Use read-modify-write instructions
+CONFIG_RMW_INSNS
+  This allows code in the kernel to be generated that uses an indivisible
+  read-modify-write cycle.  While this is faster, it can conflict with
+  DMA (= direct memory access) on many systems.  It is very likely that
+  this will cause serious problems on any Amiga or Atari Medusa if set, but
+  it apparently improves performance on 68030-based Ataris.  Unless you
+  know what you are doing, or have an Atari with a 68030 (the only system on
+  which this has been tested) say N.
+
+Amiga Zorro II ramdisk support
+CONFIG_AMIGA_Z2RAM
+  This enables support for using Chip RAM and Zorro II RAM as a ramdisk
+  or as a swap partition.  Say Y if you want to include this driver in
+  the kernel.
+  This driver is also available as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt.
+
+Atari ACSI support
+CONFIG_ATARI_ACSI
+  This enables support for the Atari ACSI interface.  Say Y if you have
+  an ACSI interface, otherwise say N.
+# Someone who has a better idea of what ACSI is should rewrite that.
+
+Probe all LUNs on each ACSI device
+CONFIG_ACSI_MULTI_LUN
+  If you have a ACSI device that supports more than one LUN (Logical
+  Unit Number), e.g. a CD jukebox, you should say Y here so that all
+  will be found by the ACSI driver. An ACSI device with multiple LUNs
+  acts logically like multiple ACSI devices. The vast majority of ACSI
+  devices have only one LUN, and so most people can say N here and
+  should in fact do so, because it is safer.
+
+Atari SLM laser printer support
+CONFIG_ATARI_SLM
+  If you have an Atari SLM laser printer, say Y to include support for
+  it in the kernel.  Otherwise, say N.
+
+A3000 WD33C93A support
+CONFIG_A3000_SCSI
+  If you have an Amiga 3000 and have SCSI devices connected to the built-in
+  SCSI controller, say Y.  Otherwise, say N.
+
+A2091 WD33C93A support
+CONFIG_A2091_SCSI
+  If you have an A2091 SCSI controller, say Y.  Otherwise, say N.
+
+GVP Series II WD33C93A support
+CONFIG_GVP11_SCSI
+  If you have a Great Valley Products Series II SCSI controller, say Y.
+  Also say Y if you have a GVP A4008 SCSI controller.  Otherwise, say N.
+
+Cyberstorm SCSI support
+CONFIG_CYBERSTORM_SCSI
+  If you have an Amiga with a Phase5 Cyberstorm accelerator board and
+  the optional Cyberstorm SCSI controller, say Y.  Otherwise, say N.
+
+Atari native SCSI support
+CONFIG_ATARI_SCSI
+  If you have a SCSI controller on your Atari, say Y.  Otherwise, say N.
+  This driver is also available as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt.
+# Again, I'm out of my depth on that one
+
+Long delays for Toshiba CD-ROMs
+CONFIG_ATARI_SCSI_TOSHIBA_DELAY
+  This option increases the delay after a SCSI reset to accomodate some
+  flakey Toshiba CD-ROM drives.  Say Y if you have a Toshiba CD-ROM drive;
+  otherwise, say N.
+
+Ariadne support
+CONFIG_ARIADNE
+  If you have a VillageTronics Ariadne Ethernet adapter, say Y.  Otherwise,
+  say N.
+  This driver is also available as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt.
+
+A2065 support
+CONFIG_A2065
+  If you have a Commodore A2065 Ethernet adapter, say Y.  Otherwise, say N.
+  This driver is also available as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt.
+
+Hydra support
+CONFIG_HYDRA
+  If you have a Hydra Ethernet adapter, say Y.  Otherwise, say N.
+  This driver is also available as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt.
+
+Atari Lance support
+CONFIG_ATARILANCE
+  If you have an Atari Lance Ethernet adapter, say Y.  Otherwise, say N.
+
+BioNet-100 support
+CONFIG_ATARI_BIONET
+  If you have a BioNet-100 Ethernet adapter, say Y.  Otherwise, say N.
+
+PAMsNet support
+CONFIG_ATARI_PAMSNET
+  If you have a PAMsNet Ethernet adapter, say Y.  Otherwise, say N.
+
+Multiface Card III parallel support
+CONFIG_MULTIFACE_III_LP
+  If you have a Multiface III card for your Amiga, and want to use its
+  parallel port in Linux, say Y.  Otherwise, say N.
+  This driver is also available as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt.
+
+Amiga mouse support
+CONFIG_AMIGAMOUSE
+  If you want to be able to use an Amiga mouse in Linux, say Y.
+  This driver is also available as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt.
+
+Amiga Copper Console
+CONFIG_COPCON
+  This configures the console to use the Amiga's graphics coprocessor for
+  scrolling, instead of using the CPU.  This option markedly improves
+  response times in the high color modes (5 bitplanes and up).  If you
+  would like to use this, say Y; otherwise, say N.
+
+Atari mouse support
+CONFIG_ATARIMOUSE
+  If you want to be able to use an Atari mouse in Linux, say Y.
+  This driver is also available as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt.
+
+Atari MFP serial support
+CONFIG_ATARI_MFPSER
+  If you have an MFP serial card that you'd like to use in Linux, say Y.
+  This driver is also available as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt.
+
+Atari SCC serial support
+CONFIG_ATARI_MFPSER
+  If you have SCC serial ports that you'd like to use in Linux, say Y.
+  This driver is also available as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt.
+
+Atari MIDI serial support
+CONFIG_ATARI_MFPSER
+  If you want to use your Atari's MIDI port in Linux, say Y.
+  This driver is also available as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt.
+
+Amiga builtin serial support
+CONFIG_AMIGA_BUILTIN_SERIAL
+  If you want to use your Amiga's built-in serial port in Linux, say Y.
+  This driver is also available as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt.
+
+GVP IO-Extender support
+CONFIG_GVPIOEXT
+  If you want to use a GVP IO-Extender serial card in Linux, say Y.
+  Otherwise, say N.
+
+Multiface Card III serial support
+CONFIG_MULTIFACE_III_TTY
+  If you want to use a Multiface III card's serial port in Linux, say Y.
+  This driver is also available as a module ( = code which can be inserted
+  in and removed from the running kernel whenever you want). If you
+  want to compile it as a module, say M here and read
+  Documentation/modules.txt.
+
+Amiga or Atari DMA sound support
+CONFIG_DMASOUND
+  If you want to use the internal audio of your Atari or Amiga in Linux,
+  answer Y to this question.  This will provide a Sun-like /dev/audio,
+  compatible with the Linux/i386 sound system.  Otherwise, say N.
+
 # need an empty line after last entry, for sed script in Configure.
 
 #
diff -urN pc-2.0.21/Documentation/filesystems/affs.txt linux-2.0.21/Documentation/filesystems/affs.txt
--- pc-2.0.21/Documentation/filesystems/affs.txt	Thu Jul 25 08:08:28 1996
+++ linux-2.0.21/Documentation/filesystems/affs.txt	Sat Aug 24 14:26:48 1996
@@ -49,6 +49,9 @@
 
 setgid[=gid]	Same as above, but for gid.
 
+use_mp		The uid and gid are taken from the now covered mount point
+		instead of the current user or value defined.
+
 mode=mode	Sets the mode flags to the given (octal) value, regardless
 		of the original permissions. Directories will get an x
 		permission, if the corresponding r bit is set.
@@ -71,13 +74,6 @@
 verbose		The volume name, file system type and block size will
 		be written to the syslog.
 
-prefix=path	Path will be prefixed to every absolute path name of
-		symbolic links on an AFFS partition. Default = /
-
-volume=name	When symbolic links with an absolute path are created
-		on an AFFS partition, volume will be prepended as the
-		volume name. Default = "" (empty string).
-
 Handling of the Users/Groups and protection flags
 =================================================
 
@@ -115,33 +111,9 @@
 Newly created files and directories will get the user and group id
 of the current user and a mode according to the umask.
 
-Symbolic links
-==============
-
-Although the Amiga and Linux file systems resemble each other, there
-are some, not always subtle, differences. One of them becomes apparent
-with symbolic links. While Linux has a file system with exactly one
-root directory, the Amiga has a seperate root directory for each
-file system (i. e. partition, floppy disk, ...). With the Amiga,
-these entities are called "volumes". They have symbolic names which
-can be used to access them. Thus, symbolic links can point to a
-different volume. AFFS turns the volume name into a directory name
-and prepends the prefix path (see prefix option) to it.
-
-Example:
-You mount all your Amiga partitions under /amiga/<volume> (where
-<volume> is the name of the volume), and you give the option
-"prefix=/amiga/" when mounting all your AFFS partitions. (They
-might be "User", "WB" and "Graphics", the mount points /amiga/User,
-/amiga/WB and /amiga/Graphics). A symbolic link referring to
-"User:sc/include/dos/dos.h" will be followed to
-"/amiga/User/sc/include/dos/dos.h".
-
-Examples
-========
-
-Command line
-    mount  Archive/Amiga/Workbench3.1.adf /mnt -t affs -o loop,reserved=4
+Command line example
+====================
+    mount  Archive/Amiga/Workbench3.1.adf /mnt -t affs -o loop,size=1760
     mount  /dev/sda3 /Amiga -t affs
 
 /etc/fstab example
Binary files pc-2.0.21/Documentation/logo.gif and linux-2.0.21/Documentation/logo.gif differ
diff -urN pc-2.0.21/Documentation/m68k/amiboot.README linux-2.0.21/Documentation/m68k/amiboot.README
--- pc-2.0.21/Documentation/m68k/amiboot.README	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/Documentation/m68k/amiboot.README	Tue Sep 10 21:46:06 1996
@@ -0,0 +1,228 @@
+
+		Linux/m68k Amiga Bootstrap version 4.0
+		--------------------------------------
+
+
+0. Introduction
+---------------
+
+Amiboot is used to boot Linux/m68k on Amiga from the CLI/Shell.
+
+Before you try to boot Linux/m68k for the first time, please read the FAQ
+
+    http://www-agrw.informatik.uni-kl.de/~jmayer/linux68k/linux68k-faq
+
+and the Installation Guide
+
+    http://www.informatik.uni-oldenburg.de/~amigo/inst.html
+
+first. Although the Installation Guide is getting a bit outdated, it's still a
+good starting point.
+
+Amiboot 4.0 is meant for Linux/m68k 2.0.x or higher. Use an older version for
+older kernels.
+
+
+1. Running Amiboot
+------------------
+
+The Amiboot invocation syntax looks like
+
+    amiboot [options] [kernel command line]
+
+Valid options are:
+
+    --help           Display the usage information
+
+    --kernel file    Use kernel image `file' (default is `vmlinux')
+
+    --ramdisk file   Use ramdisk image `file'.
+
+    --debug          Enable debug mode
+
+    --memfile file   Use memory file `file'
+
+    --keep-video     Don't reset the video mode
+
+    --model id       Set the Amiga model to `id'.
+
+The kernel command line contains the options you want to pass to the kernel and
+to init, the process that's started first by Linux.
+
+Normally you only use the --kernel option to specify the file that contains the
+Linux/m68k kernel image, and --ramdisk if you want to boot from a ramdisk file,
+i.e. a file containing a complete file system, instead of from a hard disk
+partition.
+
+Example:
+
+    amiboot -k vmlinux-2.0.11 root=/dev/hda3 video=font:PEARL8x8
+
+Amiboot will boot the kernel image `vmlinux-2.0.11' and will pass
+`root=/dev/hda3 video=font:PEARL8x8' to the kernel.
+
+
+The other options are more specialized. Don't use them unless you really have
+to and you know what you're doing.
+
+The --memfile option is used to specify the blocks of memory that will be used
+by Linux.
+
+The --keep-video option is necessary if you want to retain the current graphics
+mode (on a graphics board) under Linux. Currently this is only useful if you
+have a CyberVision 64 graphics board.
+
+Finally, --model allows you to specify your Amiga model, and --debug is for
+debugging purposes.
+
+
+2. The memory file
+------------------
+
+If you have some non-AutoConfig memory you want to use under Linux, or if you
+want to disable some parts of your memory (e.g. Zorro II RAM on '040 based
+systems), you have to use a memory file and the --memfile option. This file
+contains information about the memory chunks you want to use under Linux. The
+format for the file is:
+
+    chipramsize
+    [0xfastchunkaddr fastchunksize]
+    [0xfastchunkaddr fastchunksize]
+    ...
+
+For example, if you don't want Linux to use your 2nd meg of chipram, you would
+create a file that looks contains only:
+
+    1048576
+
+If you had 1M of chip ram, 2M of 16 bit FAST ram at address 0x200000 and 16M of
+32 bit FAST ram at address 0x80000000, and you didn't want Linux to use the
+slow 16 bit FAST ram, you'd create a file that looks like:
+
+    1048576
+    0x80000000 16777216
+
+The memory file can also be used to specify in which block of memory the kernel
+will be put. Normally Amiboot will put the kernel in the first block of Fast
+RAM it will find. If you use a memory file, it will put the kernel in the first
+block of fast RAM you specify.
+
+
+3. Amiga models
+---------------
+
+If Amiboot incorrectly detects the model of your Amiga, you can force it to
+detect any model you want using the --model option. `id' must be one of the
+numbers as defined in <asm/amigatypes.h> (AMI_*). Currently the following
+models are known:
+
+    Model	    ID
+    -----	    --
+    Amiga 500	     1
+    Amiga 500+	     2
+    Amiga 600	     3
+    Amiga 1000	     4
+    Amiga 1200	     5
+    Amiga 2000	     6
+    Amiga 2500	     7
+    Amiga 3000	     8
+    Amiga 3000T	     9
+    Amiga 3000+	    10
+    Amiga 4000	    11
+    Amiga 4000T	    12
+    CDTV	    13
+    CD32	    14
+    Draco	    15
+
+Note that Amiboot can't distinguish among Amiga models that are very similar to
+each other (e.g. A500/A1000/A2000/A2500 and A3000/A3000T). Of course this is
+harmless and there's no real need to use --model in that case.
+
+Please send me the output of amiboot used with the --debug option if your Amiga
+model is detected incorrectly.
+
+
+4. Abbreviations
+----------------
+
+All options also have a shorthand:
+
+    --help 	    -h
+    --kernel	    -k
+    --ramdisk	    -r
+    --debug	    -d
+    --memfile	    -m
+    --keep-video    -v
+    --model	    -t
+
+
+5. Miscellaneous
+----------------
+
+Some expansion boards keep on generating interrupts once they were initialized
+under AmigaOS. This can cause an interrupt deadlock while booting Linux. The
+following boards are recognized and disabled:
+
+    o Helfrich Rainbow 3 Graphics Board
+    o Helfrich Piccolo Graphics Board
+    o Helfrich SD64 Graphics Board
+    o Village Tronic Ariadne Ethernet Board
+    o Hydra Systems Amiganet Ethernet Board
+
+The following boards are known to cause problems but we don't have a disable
+routine for them yet:
+
+    o Commodore A2060 Arcnet Card
+    o Ameristar A560 Arcnet Card
+
+If you write a routine to disable an expansion board, please let me know.
+
+
+6. Troubleshooting
+------------------
+
+  - Amiboot says
+
+	This bootstrap is too old/new for this kernel
+
+    This means that you're using a version of Amiboot that's not compatible
+    with the kernel you want to boot.
+
+    Solution: use the correct Amiboot, or use another kernel.
+
+  - Amiboot says
+
+	Warning: too many AutoConfig devices. Ignoring device at 0x????????
+
+    or
+
+	Warning: too many memory blocks. Ignoring block of ???K at 0x????????
+
+    This means that you have more AutoConfig devices or memory chunks than
+    Amiboot supports.
+
+    Solution: increase the NUM_AUTO (for AutoConfig devices) or NUM_MEMINFO
+    (for memory chunks) values in the kernel sources and recompile both Amiboot
+    and the kernel.
+
+  - If all you get is a grey screen, or if Linux/m68k suddenly locks up during
+    booting, try the following things:
+
+      o Boot with the Startup-Sequence disabled, run SetPatch and try again.
+
+      o If that doesn't work, remove any expansion devices and retry.
+
+
+7. Amiga-Lilo
+-------------
+
+Once you have a stable Linux/m68k installation, you may want to try Amiga-Lilo.
+Amiga-Lilo allows you to boot Linux/m68k without the overhead of booting
+AmigaOS first, and it provides you with a boot menu.
+
+
+8. Credits
+----------
+
+This readme was written by Geert Uytterhoeven. A lot of information was taken
+from the ANNOUNCE-* files by Hamish Macdonald.
diff -urN pc-2.0.21/Documentation/m68k/kernel-options.txt linux-2.0.21/Documentation/m68k/kernel-options.txt
--- pc-2.0.21/Documentation/m68k/kernel-options.txt	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/Documentation/m68k/kernel-options.txt	Thu Sep 19 00:32:17 1996
@@ -0,0 +1,836 @@
+
+
+				  Command Line Options for Linux/68k
+				  ==================================
+
+Date: Sep 14, 1996
+Linux/68k version: 2.0.20
+Author: Roman.Hodek@informatik.uni-erlangen.de (Roman Hodek)
+Update: jds@kom.auc.dk (Jes Sorensen)
+
+0) Introduction
+===============
+
+  Often I've been asked which command line options the Linux/68k
+kernel understands, or how the exact syntax for the ... option is, or
+... about the option ... . I hope, this document supplies all the
+answers...
+
+  Note that some options might be outdated, their descriptions being
+incomplete or missing. Please update the information and send in the
+patches.
+
+
+1) Overview of the Kernel's Option Processing
+=============================================
+
+The kernel knows three kinds of options on its command line:
+
+  1) kernel options
+  2) environment settings
+  3) arguments for init
+
+To which of these classes an argument belongs is determined as
+follows: If the option is known to the kernel itself, i.e. if the name
+(the part before the '=') or, in some cases, the whole argument string
+is known to the kernel, it belongs to class 1. Otherwise, if the
+argument contains an '=', it is of class 2, and the definition is put
+into init's environment. All other arguments are passed to init as
+command line options.
+
+  This document describes the valid kernel options for Linux/68k in
+the version mentioned at the start of this file. Later revisions may
+add new such options, and some may be missing in older versions.
+
+  In general, the value (the part after the '=') of an option is a
+list of values separated by commas. The interpretation of these values
+is up to the driver that "owns" the option. This association of
+options with drivers is also the reason that some are further
+subdivided.
+
+
+2) General Kernel Options
+=========================
+
+2.1) root=
+----------
+
+Syntax: root=/dev/<device>
+    or: root=<hex_number>
+
+This tells the kernel which device it should mount as the root
+filesystem. The device must be a block device with a valid filesystem
+on it.
+
+  The first syntax gives the device by name. These names are converted
+into a major/minor number internally in the kernel in an unusual way.
+Normally, this "conversion" is done by the device files in /dev, but
+this isn't possible here, because the root filesystem (with /dev)
+isn't mounted yet... So the kernel parses the name itself, with some
+hardcoded name to number mappings. The name must always be a
+combination of two or three letters, followed by a decimal number.
+Valid names are:
+
+  /dev/ram: -> 0x0100 (initial ramdisk)
+  /dev/hda: -> 0x0300 (first IDE disk)
+  /dev/hdb: -> 0x0340 (second IDE disk)
+  /dev/sda: -> 0x0800 (first SCSI disk)
+  /dev/sdb: -> 0x0810 (second SCSI disk)
+  /dev/sdc: -> 0x0820 (third SCSI disk)
+  /dev/sdd: -> 0x0830 (forth SCSI disk)
+  /dev/sde: -> 0x0840 (fifth SCSI disk)
+  /dev/fd : -> 0x0200 (floppy disk)
+  /dev/xda: -> 0x0c00 (first XT disk, unused in Linux/68k)
+  /dev/xdb: -> 0x0c40 (second XT disk, unused in Linux/68k)
+  /dev/ada: -> 0x1c00 (first ACSI device)
+  /dev/adb: -> 0x1c10 (second ACSI device)
+  /dev/adc: -> 0x1c20 (third ACSI device)
+  /dev/add: -> 0x1c30 (forth ACSI device)
+
+The last for names are available only if the kernel has been compiled
+with Atari and ACSI support.
+
+  The name must be followed by a decimal number, that stands for the
+partition number of times. Internally, the value of the number is just
+added to the device number mentioned in the table above. The
+exceptions are /dev/ram and /dev/fd, where /dev/ram refers to an
+initial ramdisk loaded by your bootstrap program (please consult the
+instructions for your bootstrap program to find out how to load an
+initial ramdisk). As of kernel version 2.0.18 you must specify
+/dev/ram as the root device if you want to boot from an initial
+ramdisk. For the floppy devices, /dev/fd, the number stands for the
+floppy drive number (there are no partitions on floppy disks). I.e.,
+/dev/fd0 stands for the first drive, /dev/fd1 for the second, and so
+on. Since the number is just added, you can also force the disk format
+by adding a number greater than 3. If you look into your /dev
+directory, use can see the /dev/fd0D720 has major 2 and minor 16. You
+can specify this device for the root FS by writing "root=/dev/fd16" on
+the kernel command line.
+
+[Strange and maybe uninteresting stuff ON]
+
+  This unusual translation of device names has some strange
+consequences: If, for example, you have a symbolic link from /dev/fd
+to /dev/fd0D720 as an abbreviation for floppy driver #0 in DD format,
+you cannot use this name for specifying the root device, because the
+kernel cannot see this symlink before mounting the root FS and it
+isn't in the table above. If you would use it, the root device weren't
+set at all, without error message. Another example: You cannot use a
+partition on e.g. the sixth SCSI disk as the root filesystem, if you
+want to specify it by name. This is, because only the devices up to
+/dev/sde are in the table above, but not /dev/sdf. Although, you can
+use the sixth SCSI disk for the root FS, but you have to specify the
+device by number... (see below). Or, even more strange, use can use the
+fact that there is no range checking of the partition number, and your
+knowledge that each disk uses 16 minors, and write "root=/dev/sde17"
+(for /dev/sdf1).
+
+[Strange and maybe uninteresting stuff OFF]
+
+  If the device containing your root partition isn't in the table
+above, you can also specify it by major and minor numbers. These are
+written in hex, with no prefix and no separator between. E.g., if you
+have a CD with contents appropriate as a root filesystem in the first
+SCSI CD-ROM drive, you boot from it by "root=0b00". Here, hex "0b" =
+decimal 11 is the major of SCSI CD-ROMs, and the minor 0 stands for
+the first of these. You can find out all valid major numbers by
+looking into include/linux/major.h.
+
+
+2.2) ro, rw
+-----------
+
+Syntax: ro
+    or: rw
+
+These two options tell the kernel whether it should mount the root
+filesystem read-only or read-write. The default is read-only, except
+for ramdisks which are read-write.
+
+
+2.3) debug
+----------
+
+Syntax: debug
+
+This raises the kernel log level to 10 (the default is 7). This is the
+same level as set by the "dmsg" command, just that the maximum level
+selectable by dmesg is 8.
+
+
+2.4) debug=
+-----------
+
+Syntax: debug=<device>
+
+This option causes certain kernel messages be printed to the selected
+debugging device. This can aid debugging the kernel, since the
+messages can be captured and analyzed on some other machine. Which
+devices are possible depends on the machine type. There are no checks
+for the validity of the device name. If the device isn't implemented,
+nothing happens.
+
+  Messages logged this way are in general stack dumps after kernel
+memory faults or bad kernel traps, and kernel panics. To be exact: all
+messages of level 0 (panic messages) and all messages printed while
+the log level is 8 or more (their level doesn't matter). Before stack
+dumps, the kernel sets the log level to 10 automatically. A level of
+at least 8 can also be set by the "debug" command line option (see
+2.3) and at run time with "dmesg -n 8".
+
+Devices possible for Amiga:
+
+ - "ser": built-in serial port; parameters: 9600bps, 8N1
+ - "mem": Save the messages to a reserved area in chip mem. After
+          rebooting, they can be read under AmigaOS with the tool
+          'dmesg'.
+
+Devices possible for Atari:
+
+ - "ser1": ST-MFP serial port ("Modem1"); parameters: 9600bps, 8N1
+ - "ser2": SCC channel B serial port ("Modem2"); parameters: 9600bps, 8N1
+ - "ser" : default serial port
+           This is "ser2" for a Falcon, and "ser1" for any other machine
+ - "par" : parallel port
+           The printing routine for this implements a timeout for the
+           case there's no printer connected (else the kernel would
+           lock up). The timeout is not exact, but usually a few
+           seconds.
+
+
+2.6) ramdisk=
+-------------
+
+Syntax: ramdisk=<size>
+
+  This option instructs the kernel to set up a ramdisk of the given
+size in KBytes. Do not use this option if the ramdisk contents are
+passed by bootstrap! In this case, the size is selected automatically
+and should not be overwritten.
+
+  The only application is for root filesystems on floppy disks, that
+should be loaded into memory. To do that, select the corresponding
+size of the disk as ramdisk size, and set the root device to the disk
+drive (with "root=").
+
+
+2.7) swap=
+-----------
+
+Syntax: swap=<max_age>,<adv>,<decl>,<init_age>,<cl_fract>,<cl_min>,\
+        <pgout_wgt>,<bfout_wgt>
+(All optional)
+
+TODO
+
+
+2.8) buff=
+-----------
+
+Syntax: buff=<max_age>,<adv>,<decl>,<init_age>,<bfout_wgt>,<mem_grace>
+(All optional)
+
+TODO
+
+
+
+3) General Device Options (Amiga and Atari)
+===========================================
+
+3.1) ether=
+-----------
+
+Syntax: ether=[<irq>[,<base_addr>[,<mem_start>[,<mem_end>]]]],<dev-name>
+
+  <dev-name> is the name of a net driver, as specified in
+drivers/net/Space.c in the Linux source. Most prominent are eth0, ...
+eth3, sl0, ... sl3, ppp0, ..., ppp3, dummy, and lo.
+
+  The non-ethernet drivers (sl, ppp, dummy, lo) obviously ignore the
+settings by this options. Also, the existing ethernet drivers for
+Linux/68k (ariadne, a2065, hydra) don't use them because Zorro boards
+are really Plug-'n-Play, so the "ether=" option is useless altogether
+for Linux/68k.
+
+
+3.2) hd=
+--------
+
+Syntax: hd=<cylinders>,<heads>,<sectors>
+
+  This option sets the disk geometry of an IDE disk. The first hd=
+option is first the first IDE disk, the second for the second one.
+(I.e., you can give this option twice.) In most cases, you won't have
+to use this option, since the kernel can obtain the geometry data
+itself. It exists just for the case that this fails for one of your
+disks.
+
+
+3.3) max_scsi_luns=
+-------------------
+
+Syntax: max_scsi_luns=<n>
+
+  Sets the maximum number of LUNs (logical units) of SCSI devices to
+be scanned. Valid values for <n> are between 1 and 8. Default is 8 if
+"Probe all LUNs on each SCSI device" was selected during the kernel
+configuration, else 1.
+
+
+3.4) st=
+--------
+
+Syntax: st=<buffer_size>,[<write_thres>,[<max_buffers>]]
+
+  Sets several parameters of the SCSI tape driver. <buffer_size> is
+the number of 512-byte buffers reserved for tape operations for each
+device. <write_thres> sets the number of blocks which must be filled
+to start an actual write operation to the tape. Maximum value is the
+total number of buffers. <max_buffer> limits the total number of
+buffers allocated for all tape devices.
+
+
+3.5) dmasound=
+--------------
+
+Syntax: dmasound=[<buffers>,<buffer-size>[,<catch-radius>]]
+
+  This option controls some configurations of the m68k DMA sound
+driver (Amiga and Atari): <buffers> is the number of buffers you want
+to use (minimum 4, default 4), <buffer-size> is the size of each
+buffer in kilobytes (minimum 4, default 32) and <catch-radius> says
+how much percent of error will be tolerated when setting a frequency
+(maximum 10, default 0). For example with 3% you can play 8000Hz
+AU-Files on the Falcon with its hardware frequency of 8195Hz and thus
+don't need to expand the sound.
+
+
+
+4) Options for Atari Only
+=========================
+
+4.1) video=
+--------------
+
+Syntax: video=<sub-options...>
+
+The <sub-options> is a comma-separated list of the sub-options listed
+below.
+
+NB: Please notice that this option was renamed from `atavideo' to
+    `video' during the development of the 1.3.x kernels, thus you
+    might need to update your boot-scripts if upgrading to 2.0.x from
+    an 1.2.13ply kernel.
+
+4.1.1) Video Mode
+-----------------
+
+This sub-option may be any of the predefined video modes, as listed
+in atari/atafb.c in the Linux/68k source tree. The kernel will
+activate the given video mode at boot time and make it the default
+mode, if the hardware allows. Currently defined names are:
+
+ - stlow           : 320x200x4
+ - stmid, default5 : 640x200x2
+ - sthigh, default4: 640x400x1
+ - ttlow           : 320x480x8, TT only
+ - ttmid, default1 : 640x480x4, TT only
+ - tthigh, default2: 1280x960x1, TT only
+ - vga2            : 640x480x1, Falcon only
+ - vga4            : 640x480x2, Falcon only
+ - vga16, default3 : 640x480x4, Falcon only
+ - vga256          : 640x480x8, Falcon only
+ - falh2           : 896x608x1, Falcon only
+ - falh16          : 896x608x4, Falcon only
+
+  If no video mode is given on the command line, the kernel tries the
+modes names "default<n>" in turn, until one is possible with the
+hardware in use.
+
+  A video mode setting doesn't make sense, if the external driver is
+activated by a "external:" sub-option.
+
+4.1.2) inverse
+--------------
+
+Invert the display. This affects both, text (consoles) and graphics
+(X) display. Usually, the background is chosen to be black. With this
+option, you can make the background white.
+
+4.1.3) font
+-----------
+
+Syntax: font:<fontname>
+
+Specify the font to use in text modes. Currently you can choose only
+between `VGA8x8' and `VGA8x16'. `VGA8x8' is default, if the vertical
+size of the display is less than 400 pixel rows. Otherwise, the `VGA8x16'
+font is the default.
+
+4.1.4) hwscroll_
+----------------
+
+Syntax: hwscroll_<n>
+
+The number of additional lines of video memory to reserve for
+speeding up the scrolling ("hardware scrolling"). Hardware scrolling
+is possible only if the kernel can set the video base address in steps
+fine enough. This is true for STE, MegaSTE, TT, and Falcon. It is not
+possible with plain STs and graphics cards (The former because the
+base address must be on a 256 byte boundary there, the latter because
+the kernel doesn't know how to set the base address at all.)
+
+  By default, <n> is set to the number of visible text lines on the
+display. Thus, the amount of video memory is doubled, compared to no
+hardware scrolling. You can turn off the hardware scrolling altogether
+by setting <n> to 0.
+
+4.1.5) internal:
+----------------
+
+Syntax: internal:<xres>;<yres>[;<xres_max>;<yres_max>;<offset>]
+
+This option specifies the capabilities of some extended internal video
+hardware, like e.g. OverScan. <xres> and <yres> give the (extended)
+dimensions of the screen.
+
+  If your OverScan needs a black border, you have to write the last
+three arguments of the "internal:". <xres_max> is the maximum line
+length the hardware allows, <yres_max> the maximum number of lines.
+<offset> is the offset of the visible part of the screen memory to its
+physical start, in bytes.
+
+  Often, extended interval video hardware has to be activated somehow.
+For this, see the "sw_*" options below.
+
+4.1.6) sw_acia, sw_snd6, sw_snd7
+--------------------------------
+
+This specifies the method for turning on extended internal video
+hardware, like OverScan. Several methods are in use:
+
+  sw_acia: Set RTS of the keyboard ACIA high
+  sw_snd6: Set bit 6 of the PSG port A
+  sw_snd7: Set bit 7 of the PSG port A
+
+These sub-options are generally only useful together with "internal:".
+
+4.1.7) external:
+----------------
+
+Syntax:
+  external:<xres>;<yres>;<depth>;<org>;<scrmem>[;<scrlen>[;<vgabase>\
+           [;<colw>[;<coltype>]]]]
+
+[I had to break this line...]
+
+  This is probably the most complicated parameter... It specifies that
+you have some external video hardware (a graphics board), and how to
+use it under Linux/68k. The kernel cannot know more about the hardware
+than you tell it here! The kernel also is unable to set or change any
+video modes, since it doesn't know about any board internal. So, you
+have to switch to that video mode before you start Linux, and cannot
+switch to another mode once Linux has started.
+
+  The first 3 parameters of this sub-option should be obvious: <xres>,
+<yres> and <depth> give the dimensions of the screen and the number of
+planes (depth). The depth is is the logarithm to base 2 of the number
+of colors possible. (Or, the other way round: The number of colors is
+2^depth).
+
+  You have to tell the kernel furthermore how the video memory is
+organized. This is done by a letter as <org> parameter:
+
+ 'n': "normal planes", i.e. one whole plane after another
+ 'i': "interleaved planes", i.e. 16 bit of the first plane, than 16 bit
+      of the next, and so on... This mode is used only with the
+	  built-in Atari video modes, I think there is no card that
+	  supports this mode.
+ 'p': "packed pixels", i.e. <depth> consecutive bits stand for all
+	  planes of one pixel; this is the most common mode for 8 planes
+	  (256 colors) on graphic cards
+ 't': "true color" (more or less packed pixels, but without a color
+	  lookup table); usually depth is 24
+
+For monochrome modes (i.e., <depth> is 1), the <org> letter has a
+different meaning:
+
+ 'n': normal colors, i.e. 0=white, 1=black
+ 'i': inverted colors, i.e. 0=black, 1=white
+
+  The next important information about the video hardware is the base
+address of the video memory. That is given in the <scrmem> parameter,
+as a hexadecimal number with a "0x" prefix. You have to find out this
+address in the documentation of your hardware.
+
+  The next parameter, <scrlen>, tells the kernel about the size of the
+video memory. If it's missing, the size is calculated from <xres>,
+<yres>, and <depth>. For now, it is not useful to write a value here.
+It would be used only for hardware scrolling (which isn't possible
+with the external driver, because the kernel cannot set the video base
+address), or for virtual resolutions under X (which the X server
+doesn't support yet). So, it's currently best to leave this field
+empty, either by ending the "external:" after the video address or by
+writing two consecutive semicolons, if you want to give a <vgabase>
+(it is allowed to leave this parameter empty).
+
+  The <vgabase> parameter is optional. If it is not given, the kernel
+cannot read or write any color registers of the video hardware, and
+thus you have to set appropriate colors before you start Linux. But if
+your card is somehow VGA compatible, you can tell the kernel the base
+address of the VGA register set, so it can change the color lookup
+table. You have to look up this address in your board's documentation.
+To avoid misunderstandings: <vgabase> is the _base_ address, i.e. a 4k
+aligned address. For read/writing the color registers, the kernel
+uses the addresses vgabase+0x3c7...vgabase+0x3c9. The <vgabase>
+parameter is written in hexadecimal with a "0x" prefix, just as
+<scrmem>.
+
+  <colw> is meaningful only if <vgabase> is specified. It tells the
+kernel how wide each of the color register is, i.e. the number of bits
+per single color (red/green/blue). Default is 6, another quite usual
+value is 8.
+
+  Also <coltype> is used together with <vgabase>. It tells the kernel
+about the color register model of your gfx board. Currently, the types
+"vga" (which is also the default) and "mv300" (SANG MV300) are
+implemented.
+
+4.1.8) pwrsave
+--------------
+
+Use the power saving capabilities of the monitor. This means, in
+addition to the usual screen blanking, the HSYNC signal is turned off.
+Use this only with a monitor that supports the VESA suspend mode! This
+sub-option currently works only with the Falcon.
+
+4.1.9) eclock:
+--------------
+
+The external pixel clock attached to the Falcon VIDEL shifter. This
+currently works only with the ScreenWonder!
+
+4.1.10) monitorcap:
+-------------------
+
+Syntax: monitorcap:<vmin>;<vmax>;<hmin>;<hmax>
+
+This describes the capabilities of a multisync monitor. Don't use it
+with a fixed-frequency monitor! For now, only the Falcon frame buffer
+uses the settings of "monitorcap:".
+
+  <vmin> and <vmax> are the minimum and maximum, resp., vertical frequencies
+your monitor can work with, in Hz. <hmin> and <hmax> are the same for
+the horizontal frequency, in kHz.
+
+  The defaults are 58;62;31;32 (VGA compatible).
+
+4.1.11) keep
+------------
+
+If this option is given, the framebuffer device doesn't do any video
+mode calculations and settings on its own. The only Atari fb device
+that does this currently is the Falcon.
+
+  What you reach with this: Settings for unknown video extensions
+aren't overridden by the driver, so you can still use the mode found
+when booting, when the driver doesn't know to set this mode itself.
+But this also means, that you can't switch video modes anymore...
+
+  An example where you may want to use "keep" is the ScreenBlaster for
+the Falcon.
+
+
+4.2) atamouse=
+--------------
+
+Syntax: atamouse=<x-threshold>,[<y-threshold>]
+
+  With this option, you can set the mouse movement reporting threshold.
+This is the number of pixels of mouse movement that have to accumulate
+before the IKBD sends a new mouse packet to the kernel. Higher values
+reduce the mouse interrupt load and thus reduce the chance of keyboard
+overruns. Lower values give a slightly faster mouse responses and
+slightly better mouse tracking.
+
+  You can set the threshold in x and y separately, but usually this is
+of little practical use. If there's just one number in the option, it
+is used for both dimensions. The default value is 2 for both
+thresholds.
+
+
+4.3) ataflop=
+-------------
+
+Syntax: ataflop=<drive type>[,<trackbuffering>[,<steprateA>[,<steprateB>]]]
+
+   The drive type may be 0, 1, or 2, for DD, HD, and ED, resp. This
+   setting affects how much buffers are reserved and which formats are
+   probed (see also below). The default is 1 (HD). Only one drive type
+   can be selected. If you have two disk drives, select the "better"
+   type.
+
+   The second parameter <trackbuffer> tells the kernel whether to use
+   track buffering (1) or not (0). The default is machine dependant:
+   no for the Medusa and yes for all others.
+
+   With the two following parameters, you can change the default
+   steprate used for drive A and B, resp. 
+
+
+4.4) atascsi=
+-------------
+
+Syntax: atascsi=<can_queue>[,<cmd_per_lun>[,<scat-gat>[,<host-id>[,<tagged>]]]]
+
+  This option sets some parameters for the Atari native SCSI driver.
+Generally, any number of arguments can be omitted from the end. And
+for each of the numbers, a negative value means "use default". The
+defaults depend on whether TT-style or Falcon-style SCSI is used.
+Below, defaults are noted as n/m, where the first value refers to
+TT-SCSI and the latter to Falcon-SCSI. If an illegal value is given
+for one parameter, an error message is printed and that one setting is
+ignored (other aren't affected).
+
+  <can_queue>:
+    This is the maximum number of SCSI commands queued internal to the
+    Atari SCSI driver. A value of 1 effectively turns off the driver
+    internal multitasking (if it makes problems). Legal values are >=
+    1. <can_queue> can be as high as you like, but values greater than
+    <cmd_per_lun> times the number of SCSI targets (LUNs) you have
+    don't make sense. Default: 16/8.
+
+  <cmd_per_lun>:
+    Maximum number of SCSI commands issued to the driver for one
+    logical unit (LUN, usually one SCSI target). Legal values start
+    from 1. If tagged queuing (see below) is not used, values greater
+    than 2 don't make sense, but waste memory. Otherwise, the maximum
+    is the number of command tags available to the driver (currently
+    32). Default: 8/1. (Note: Values > 1 seem to cause problems on a
+    Falcon, cause not yet known.)
+
+      The <cmd_per_lun> value at a great part determines the amount of
+    memory SCSI reserves for itself. The formula is rather
+    complicated, but I can give you some hints:
+      no scatter-gather  : cmd_per_lun * 232 bytes
+      full scatter-gather: cmd_per_lun * approx. 17 Kbytes
+
+  <scat-gat>:
+    Size of the scatter-gather table, i.e. the number of requests
+    consecutive on the disk that can be merged into one SCSI command.
+    Legal values are between 0 and 255. Default: 255/0. Note: This
+    value is forced to 0 on a Falcon, since scatter-gather isn't
+    possible with the ST-DMA. Not using scatter-gather hurts
+    perfomance significantly.
+
+  <host-id>:
+    The SCSI ID to be used by the initiator (your Atari). This is
+    usually 7, the highest possible ID. Every ID on the SCSI bus must
+    be unique. Default: determined at run time: If the NV-RAM checksum
+    is valid, and bit 7 in byte 30 of the NV-RAM is set, the lower 3
+    bits of this byte are used as the host ID. (This method is defined
+    by Atari and also used by some TOS HD drivers.) If the above
+    isn't given, the default ID is 7. (both, TT and Falcon).
+
+  <tagged>:
+    0 means turn off tagged queuing support, all other values > 0 mean
+    use tagged queuing for targets that support it. Default: currently
+    off, but this may change when tagged queuing handling has been
+    proofed to be reliable.
+
+    Tagged queuing means that more than one command can be issued to
+    one LUN, and the SCSI device itself orders the requests so they
+    can be performed in optimal order. Not all SCSI devices support
+    tagged queuing (:-().
+
+
+
+5) Options for Amiga Only:
+==========================
+
+5.1) video=
+-----------
+
+Syntax: video=<sub-options...>
+
+The <sub-options> is a comma-separated list of the sub-options listed
+below. This option is organized similar to the Atari version of the
+"video"-option (4.1), but knows fewer sub-options.
+
+5.1.1) video mode
+-----------------
+
+Again, similar to the video mode for the Atari (see 4.1.1). Predefined
+modes depend on the used frame buffer device.
+
+OCS, ECS and AGA machines all use the color frame buffer. The following
+predefined video modes are available:
+
+NTSC modes:
+ - ntsc            : 640x200, 15 kHz, 60 Hz
+ - ntsc-lace       : 640x400, 15 kHz, 60 Hz interlaced
+PAL modes:
+ - pal             : 640x256, 15 kHz, 50 Hz
+ - pal-lace        : 640x512, 15 kHz, 50 Hz interlaced
+ECS modes:
+ - multiscan       : 640x480, 29 kHz, 57 Hz
+ - multiscan-lace  : 640x960, 29 kHz, 57 Hz interlaced
+ - euro36          : 640x200, 15 kHz, 72 Hz
+ - euro36-lace     : 640x400, 15 kHz, 72 Hz interlaced
+ - euro72          : 640x400, 29 kHz, 68 Hz
+ - euro72-lace     : 640x800, 29 kHz, 68 Hz interlaced
+ - super72         : 800x300, 23 kHz, 70 Hz
+ - super72-lace    : 800x600, 23 kHz, 70 Hz interlaced
+ - dblntsc-ff      : 640x400, 27 kHz, 57 Hz
+ - dblntsc-lace    : 640x800, 27 kHz, 57 Hz interlaced
+ - dblpal-ff       : 640x512, 27 kHz, 47 Hz
+ - dblpal-lace     : 640x1024, 27 kHz, 47 Hz interlaced
+ - dblntsc         : 640x200, 27 kHz, 57 Hz doublescan
+ - dblpal          : 640x256, 27 kHz, 47 Hz doublescan
+VGA modes:
+ - vga             : 640x480, 31 kHz, 60 Hz
+ - vga70           : 640x400, 31 kHz, 70 Hz
+
+Please notice that the ECS and VGA modes require either an ECS or AGA
+chipset, and that these modes are limited to 2-bit color for the ECS
+chipset and 8-bit color for the AGA chipset.
+
+5.1.2) depth
+------------
+
+Syntax: depth:<nr. of bit-planes>
+
+Specify the number of bit-planes for the selected video-mode.
+
+5.1.3) inverse
+--------------
+
+Use inverted display. Functionally the same as the "inverse"
+sub-option for the Atari.
+
+5.1.4) font
+-----------
+
+Syntax: font:<fontname>
+
+Specify the font to use in text modes. Functionally the same as the
+"font" sub-option for the Atari.
+
+5.1.5) pwrsave
+--------------
+
+Use the power saving capabilities of the monitor. This means, in
+addition to the usual screen blanking, the HSYNC signal is turned off.
+Use this only with a monitor that supports the VESA suspend mode! This
+sub-option currently works only with the color frame buffer, and only
+for non-PAL/NTSC video modes.
+
+5.1.6) monitorcap:
+-------------------
+
+Syntax: monitorcap:<vmin>;<vmax>;<hmin>;<hmax>
+
+This describes the capabilities of a multisync monitor. For now, only
+the color frame buffer uses the settings of "monitorcap:".
+
+  <vmin> and <vmax> are the minimum and maximum, resp., vertical frequencies
+your monitor can work with, in Hz. <hmin> and <hmax> are the same for
+the horizontal frequency, in kHz.
+
+  The defaults are 50;90;15;38 (Generic Amiga monitor).
+
+
+5.2) fd_def_df0=
+----------------
+
+Syntax: fd_def_df0=<value>
+
+Sets the df0 value for "silent" floppy drives. The value should be in
+hexadecimal with "0x" prefix.
+
+
+5.3) wd33c93=
+-------------
+
+Syntax: wd33c93=<sub-options...>
+
+The <sub-options> is a comma-separated list of the sub-options listed
+below.
+
+5.3.1) nosync
+-------------
+
+Syntax: nosync:bitmask
+
+  bitmask is a byte where the 1st 7 bits correspond with the 7
+possible SCSI devices. Set a bit to prevent sync negotiation on that
+device. To maintain backwards compatibility, a command-line such as
+"wd33c93=255" will be automatically translated to
+"wd33c93=nosync:0xff". The default is to disable sync negotiation for
+all devices, eg. nosync:0xff.
+
+5.3.2) period
+-------------
+
+Syntax: period:ns
+
+  `ns' is the minimum # of nanoseconds in a SCSI data transfer
+period. Default is 500; acceptable values are 250 - 1000.
+
+5.3.3) disconnect
+-----------------
+
+Syntax: disconnect:x
+
+  Specify x = 0 to never allow disconnects, 2 to always allow them.
+x = 1 does 'adaptive' disconnects, which is the default and generally
+the best choice.
+
+5.3.4) debug
+------------
+
+Syntax: debug:x
+
+  If `DEBUGGING_ON' is defined, x is a bit mask that causes various
+types of debug output to printed - see the DB_xxx defines in
+wd33c93.h.
+
+5.3.5) clock
+------------
+
+Syntax: clock:x
+
+  x = clock input in MHz for WD33c93 chip. Normal values would be from
+8 through 20. The default value depends on your hostadapter(s),
+default for the A3000 internal controller is 14, for the A2091 its 8
+and for the GVP hostadapters its either 8 or 14, depending on the
+hostadapter and the SCSI-clock jumper present on some GVP
+hostadapters.
+
+5.3.6) next
+-----------
+
+  No argument. Used to separate blocks of keywords when there's more
+than one host adapter in the system.
+
+
+5.4) gvp11=
+-----------
+
+Syntax: gvp11=<addr-mask>
+
+  The address mask set by this option specifies which addresses are
+valid for DMA with the GVP Series II SCSI controller. An address is
+valid, if no bits are set except the bits that are set in the mask,
+too.
+
+  Some versions of the GVP can only DMA into a 24 bit address range,
+others can use the whole 32 bit address range for DMA. The
+(conservative) default is the 24 bit version, by a default mask of
+0x00fffffe. If your GVP can handle 32 bit DMA, you can specify
+gvp11=0xfffffffe.
+
+
+/* Local Variables: */
+/* mode: text       */
+/* End:             */
diff -urN pc-2.0.21/Makefile linux-2.0.21/Makefile
--- pc-2.0.21/Makefile	Fri Sep 20 15:08:47 1996
+++ linux-2.0.21/Makefile	Mon Sep 23 22:31:22 1996
@@ -2,7 +2,7 @@
 PATCHLEVEL = 0
 SUBLEVEL = 21
 
-ARCH = i386
+ARCH = m68k
 
 #
 # For SMP kernels, set this. We don't want to have this in the config file
diff -urN pc-2.0.21/arch/m68k/Makefile linux-2.0.21/arch/m68k/Makefile
--- pc-2.0.21/arch/m68k/Makefile	Sat Mar 23 15:41:07 1996
+++ linux-2.0.21/arch/m68k/Makefile	Sun Sep  8 00:19:52 1996
@@ -21,7 +21,7 @@
 # set up for cross compiling
 COMPILE_ARCH = $(shell uname -m)
 ifneq ($(COMPILE_ARCH),$(ARCH))
-	CROSSDIR=/usr/$(ARCH)-linux
+	CROSSDIR=/usr/$(ARCH)-linux/bin
 	CC := $(CROSSDIR)/$(CC)
 	AS := $(CROSSDIR)/$(AS)
 	LD := $(CROSSDIR)/$(LD)
@@ -50,12 +50,16 @@
 else
 LINKFLAGS = -qmagic -Ttext 0xFE0
 endif
+
 CFLAGS := $(CFLAGS) -pipe
 
+ifdef CONFIG_OPTIMIZE_040
+CFLAGS := $(CFLAGS) -m68040
+endif
+
 HEAD := arch/m68k/kernel/head.o
 
 SUBDIRS += arch/m68k/kernel arch/m68k/mm arch/m68k/console arch/m68k/lib
-#SUBDIRS += arch/m68k/kernel arch/m68k/mm arch/m68k/lib
 ARCHIVES := arch/m68k/kernel/kernel.o arch/m68k/mm/mm.o $(ARCHIVES)
 LIBS += arch/m68k/lib/lib.a
 
@@ -77,12 +81,12 @@
 # add in console.a after {amiga,atari}.o that need it
 ARCHIVES := $(ARCHIVES) arch/m68k/console/console.a
 
-ifdef CONFIG_FPSP_040
+ifdef CONFIG_M68040
 ARCHIVES := $(ARCHIVES) arch/m68k/fpsp040/fpsp.o
 SUBDIRS := $(SUBDIRS) arch/m68k/fpsp040
 endif
 
-ifdef CONFIG_IFPSP_060
+ifdef CONFIG_M68060
 ARCHIVES := $(ARCHIVES) arch/m68k/ifpsp060/ifpsp.o
 SUBDIRS := $(SUBDIRS) arch/m68k/ifpsp060
 endif
diff -urN pc-2.0.21/arch/m68k/Makefile_elf linux-2.0.21/arch/m68k/Makefile_elf
--- pc-2.0.21/arch/m68k/Makefile_elf	Wed Dec 27 21:51:18 1995
+++ linux-2.0.21/arch/m68k/Makefile_elf	Thu Jan  1 01:00:00 1970
@@ -1,91 +0,0 @@
-#
-# m68k/Makefile
-#
-# This file is included by the global makefile so that you can add your own
-# architecture-specific flags and dependencies. Remember to do have actions
-# for "archclean" and "archdep" for cleaning up and making dependencies for
-# this architecture
-#
-# 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.
-#
-# Copyright (C) 1994 by Hamish Macdonald
-#
-
-# override top level makefile
-AS	= as -m68020 
-#CC	:= $(CC) -b m68kelf
-LD	= ld -m m68kelf
-
-
-#
-# Set these to indicate how to link it..
-#
-# -zmagic:   
-#
-# LINKFLAGS    = -Ttext 0x100000
-#
-# -qmagic (we need to remove the 32 byte header for bootup purposes)
-#
-
-LINKFLAGS = -qmagic -Ttext 0xFE0
-
-HEAD := arch/m68k/kernel/head.o
-
-SUBDIRS := $(SUBDIRS) arch/m68k/kernel arch/m68k/mm arch/m68k/lib
-ARCHIVES := arch/m68k/kernel/kernel.o arch/m68k/mm/mm.o $(ARCHIVES)
-LIBS := $(TOPDIR)/arch/m68k/lib/lib.a $(LIBS) $(TOPDIR)/arch/m68k/lib/lib.a
-
-ifdef CONFIG_AMIGA
-ARCHIVES := $(ARCHIVES) arch/m68k/amiga/amiga.o
-SUBDIRS := $(SUBDIRS) arch/m68k/amiga
-endif
-
-ifdef CONFIG_ATARI
-ARCHIVES := $(ARCHIVES) arch/m68k/atari/atari.o
-SUBDIRS := $(SUBDIRS) arch/m68k/atari
-endif
-
-ifdef CONFIG_MAC
-ARCHIVES := $(ARCHIVES) arch/m68k/mac/mac.o
-SUBDIRS := $(SUBDIRS) arch/m68k/mac
-endif
-
-ifdef CONFIG_FPSP_040
-ARCHIVES := $(ARCHIVES) arch/m68k/fpsp040/fpsp.o
-SUBDIRS := $(SUBDIRS) arch/m68k/fpsp040
-endif
-
-arch/m68k/kernel: dummy
-	$(MAKE) linuxsubdirs SUBDIRS=arch/m68k/kernel
-
-arch/m68k/mm: dummy
-	$(MAKE) linuxsubdirs SUBDIRS=arch/m68k/mm
-
-arch/m68k/lib: dummy
-	$(MAKE) linuxsubdirs SUBDIRS=arch/m68k/lib
-
-arch/m68k/amiga: dummy
-	$(MAKE) linuxsubdirs SUBDIRS=arch/m68k/amiga
-
-arch/m68k/atari: dummy
-	$(MAKE) linuxsubdirs SUBDIRS=arch/m68k/atari
-
-MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
-
-lilo:	vmlinux
-	if [ -f $(INSTALL_PATH)/vmlinux ]; then mv -f $(INSTALL_PATH)/vmlinux $(INSTALL_PATH)/vmlinux.old; fi
-	if [ -f $(INSTALL_PATH)/System.map ]; then mv -f $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi
-	cat vmlinux > $(INSTALL_PATH)/vmlinux
-	cp System.map $(INSTALL_PATH)/System.map
-	if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
-
-bootstrap:
-	@$(MAKEBOOT) bootstrap
-
-archclean:
-	@$(MAKEBOOT) clean
-
-archdep:
-	$(MAKEBOOT) dep
diff -urN pc-2.0.21/arch/m68k/amiga/Makefile linux-2.0.21/arch/m68k/amiga/Makefile
--- pc-2.0.21/arch/m68k/amiga/Makefile	Mon May 20 06:54:25 1996
+++ linux-2.0.21/arch/m68k/amiga/Makefile	Sat Aug 24 14:28:48 1996
@@ -8,9 +8,8 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := amiga.o
-O_OBJS	 := config.o amikeyb.o amiints.o \
-	   chipram.o amisound.o amifb.o zorro.o
-OX_OBJS = ksyms.o
+O_OBJS   := config.o amikeyb.o amiints.o cia.o \
+            chipram.o amisound.o amifb.o zorro.o ksyms.o
 
 ifdef CONFIG_FB_CYBER
 O_OBJS := $(O_OBJS) cyberfb.o
diff -urN pc-2.0.21/arch/m68k/amiga/amifb.c linux-2.0.21/arch/m68k/amiga/amifb.c
--- pc-2.0.21/arch/m68k/amiga/amifb.c	Mon May 20 06:54:25 1996
+++ linux-2.0.21/arch/m68k/amiga/amifb.c	Mon Sep  2 23:58:12 1996
@@ -4,6 +4,8 @@
  *
  *    Copyright (C) 1995 Geert Uytterhoeven
  *
+ *          with work by Roman Zippel
+ *
  *
  * This file is based on the Atari frame buffer device (atafb.c):
  *
@@ -26,15 +28,19 @@
  *
  * History:
  *
+ *   - 24 Jul 96: Copper generates now vblank interrupt and
+ *                VESA Power Saving Protocol is fully implemented
+ *   - 14 Jul 96: Rework and hopefully last ECS bugs fixed
+ *   -  7 Mar 96: Hardware sprite support by Roman Zippel
+ *   - 18 Feb 96: OCS and ECS support by Roman Zippel
+ *                Hardware functions completely rewritten
  *   -  2 Dec 95: AGA version by Geert Uytterhoeven
  *
- *
  * 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
+ * License. See the file COPYING in the main directory of this archive
  * for more details.
  */
 
-
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/string.h>
@@ -44,38 +50,46 @@
 #include <linux/delay.h>
 #include <linux/config.h>
 #include <linux/interrupt.h>
+#include <asm/setup.h>
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <asm/irq.h>
 #include <asm/amigahw.h>
 #include <asm/amigaints.h>
-#include <asm/bootinfo.h>
 #include <linux/fb.h>
 
+#define DEBUG
 
-#undef  CONFIG_AMIFB_OCS
-#undef  CONFIG_AMIFB_ECS
-#define CONFIG_AMIFB_AGA   /* Only AGA support at the moment */
-
-#define USE_MONO_AMIFB_IF_NON_AGA
-
-
-/* -------------------- BEGIN: TODO ----------------------------------------- **
-
-
-   - scan the sources for `TODO'
-
-   - timings and monspecs can be set via the kernel command line (cfr. Atari)
-
-   - OCS and ECS
-
-   - hardware cursor
-
-   - Interlaced screen -> Interlaced sprite/hardware cursor
+#if !defined(CONFIG_AMIFB_OCS) && !defined(CONFIG_AMIFB_ECS) && !defined(CONFIG_AMIFB_AGA)
+#define CONFIG_AMIFB_OCS   /* define at least one fb driver, this will change later */
+#endif
 
+#if !defined(CONFIG_AMIFB_OCS)
+#  define IS_OCS (0)
+#elif defined(CONFIG_AMIFB_ECS) || defined(CONFIG_AMIFB_AGA)
+#  define IS_OCS (chipset == TAG_OCS)
+#else
+#  define CONFIG_AMIFB_OCS_ONLY
+#  define IS_OCS (1)
+#endif
 
-** -------------------- END: TODO ------------------------------------------- */
+#if !defined(CONFIG_AMIFB_ECS)
+#  define IS_ECS (0)
+#elif defined(CONFIG_AMIFB_OCS) || defined(CONFIG_AMIFB_AGA)
+#  define IS_ECS (chipset == TAG_ECS)
+#else
+#  define CONFIG_AMIFB_ECS_ONLY
+#  define IS_ECS (1)
+#endif
 
+#if !defined(CONFIG_AMIFB_AGA)
+#  define IS_AGA (0)
+#elif defined(CONFIG_AMIFB_OCS) || defined(CONFIG_AMIFB_ECS)
+#  define IS_AGA (chipset == TAG_AGA)
+#else
+#  define CONFIG_AMIFB_AGA_ONLY
+#  define IS_AGA (1)
+#endif
 
 /*******************************************************************************
 
@@ -184,15 +198,15 @@
    -------------------------------------------
 
    Since there are much more parameters for the Amiga display than for the
-   frame buffer interface, there must be some dependencies among the Amiga display
-   parameters. Here's what I found out:
+   frame buffer interface, there must be some dependencies among the Amiga
+   display parameters. Here's what I found out:
 
       - ddfstrt and ddfstop are best aligned to 64 pixels.
       - the chipset needs 64+4 horizontal pixels after the DMA start before the
         first pixel is output, so diwstrt_h = ddfstrt+64+4 if you want to
         display the first pixel on the line too. Increase diwstrt_h for virtual
         screen panning.
-      - the display DMA always fetches 64 pixels at a time (*).
+      - the display DMA always fetches 64 pixels at a time (fmode = 3).
       - ddfstop is ddfstrt+#pixels-64.
       - diwstop_h = diwstrt_h+xres+1. Because of the additional 1 this can be 1
         more than htotal.
@@ -205,10 +219,37 @@
         cycles from the other DMA channels (audio, floppy and Chip RAM refresh).
       - I make diwstop_h and diwstop_v as large as possible.
 
-   (*) This is for fmode = 3. Lower fmodes allow for more freedom w.r.t. the
-       timings, but they limit the maximum display depth, and cause more stress
-       on the custom chip bus.
+   General dependencies
+   --------------------
+
+      - all values are SHRES pixel (35ns)
 
+                  table 1:fetchstart  table 2:prefetch    table 3:fetchsize
+                  ------------------  ----------------    -----------------
+   Pixclock     # SHRES|HIRES|LORES # SHRES|HIRES|LORES # SHRES|HIRES|LORES
+   -------------#------+-----+------#------+-----+------#------+-----+------
+   Bus width 1x #   16 |  32 |  64  #   16 |  32 |  64  #   64 |  64 |  64
+   Bus width 2x #   32 |  64 | 128  #   32 |  64 |  64  #   64 |  64 | 128
+   Bus width 4x #   64 | 128 | 256  #   64 |  64 |  64  #   64 | 128 | 256
+
+      - chipset needs 4 pixels before the first pixel is output
+      - ddfstrt must be aligned to fetchstart (table 1)
+      - chipset needs also prefetch (table 2) to get first pixel data, so
+        ddfstrt = ((diwstrt_h-4) & -fetchstart) - prefetch
+      - for horizontal panning decrease diwstrt_h
+      - the length of a fetchline must be aligned to fetchsize (table 3)
+      - if fetchstart is smaller than fetchsize, then ddfstrt can a little bit
+        moved to optimize use of dma (usefull for OCS/ECS overscan displays)
+      - ddfstop is ddfstrt+ddfsize-fetchsize
+      - If C= didn't change anything for AGA, then at following positions the
+        dma bus is allready used:
+        ddfstrt <  48 -> memory refresh
+                <  96 -> disk dma
+                < 160 -> audio dma
+                < 192 -> sprite 0 dma
+                < 416 -> sprite dma (32 per sprite)
+      - in accordance with the hardware reference manual a hardware stop is at
+        192, but AGA (ECS?) can go below this.
 
    DMA priorities
    --------------
@@ -228,7 +269,7 @@
    Implementation
    --------------
 
-   aga_decode_var() converts the frame buffer values to the Amiga values. It's
+   ami_decode_var() converts the frame buffer values to the Amiga values. It's
    just a `straightforward' implementation of the above rules.
 
 
@@ -269,11 +310,6 @@
    Broadcast video timings
    -----------------------
 
-   Since broadcast video timings are `fixed' and only depend on the video
-   system (PAL/NTSC), hsync_len and vsync_len are not used and must be set to
-   zero. All xres/yres and margin values are defined within the `visible
-   rectangle' of the display.
-
    According to the CCIR and RETMA specifications, we have the following values:
 
    CCIR -> PAL
@@ -296,17 +332,23 @@
 
       - set the FB_SYNC_BROADCAST flag to indicate that standard broadcast
         timings are to be used.
-      - make sure upper_margin+yres+lower_margin = 576 for an interlaced, 288
-        for a non-interlaced and 144 for a doublescanned display.
-      - make sure (left_margin+xres+right_margin)*pixclock is a reasonable
-        approximation to 52.48 µs.
+      - make sure upper_margin+yres+lower_margin+vsync_len = 625 for an
+        interlaced, 312 for a non-interlaced and 156 for a doublescanned
+        display.
+      - make sure left_margin+xres+right_margin+hsync_len = 1816 for a SHRES,
+        908 for a HIRES and 454 for a LORES display.
+      - the left visible part begins at 360 (SHRES; HIRES:180, LORES:90),
+        left_margin+2*hsync_len must be greater or equal.
+      - the upper visible part begins at 48 (interlaced; non-interlaced:24,
+        doublescanned:12), upper_margin+2*vsync_len must be greater or equal.
+      - ami_encode_var() calculates margins with a hsync of 5320 ns and a vsync
+        of 4 scanlines
 
    The settings for a NTSC compatible display are straightforward.
 
    Note that in a strict sense the PAL and NTSC standards only define the
    encoding of the color part (chrominance) of the video signal and don't say
    anything about horizontal/vertical synchronization nor refresh rates.
-   But since Amigas have RGB output, this issue isn't of any importance here.
 
 
                                                             -- Geert --
@@ -314,4792 +356,3228 @@
 *******************************************************************************/
 
 
-   /*
-    *    Custom Chipset Definitions
-    */
+	/*
+	 * Custom Chipset Definitions
+	 */
 
 #define CUSTOM_OFS(fld) ((long)&((struct CUSTOM*)0)->fld)
 
+	/*
+	 * BPLCON0 -- Bitplane Control Register 0
+	 */
 
-   /*
-    *    BPLCON0 -- Bitplane Control Register 0
-    */
-
-#define BPC0_HIRES      (0x8000)
-#define BPC0_BPU2       (0x4000) /* Bit plane used count */
-#define BPC0_BPU1       (0x2000)
-#define BPC0_BPU0       (0x1000)
-#define BPC0_HAM        (0x0800) /* HAM mode */
-#define BPC0_DPF        (0x0400) /* Double playfield */
-#define BPC0_COLOR      (0x0200) /* Enable colorburst */
-#define BPC0_GAUD       (0x0100) /* Genlock audio enable */
-#define BPC0_UHRES      (0x0080) /* Ultrahi res enable */
-#define BPC0_SHRES      (0x0040) /* Super hi res mode */
-#define BPC0_BYPASS     (0x0020) /* Bypass LUT - AGA */
-#define BPC0_BPU3       (0x0010) /* AGA */
-#define BPC0_LPEN       (0x0008) /* Light pen enable */
-#define BPC0_LACE       (0x0004) /* Interlace */
-#define BPC0_ERSY       (0x0002) /* External resync */
-#define BPC0_ECSENA     (0x0001) /* ECS emulation disable */
-
-
-   /*
-    *    BPLCON2 -- Bitplane Control Register 2
-    */
-
-#define BPC2_ZDBPSEL2   (0x4000) /* Bitplane to be used for ZD - AGA */
-#define BPC2_ZDBPSEL1   (0x2000)
-#define BPC2_ZDBPSEL0   (0x1000)
-#define BPC2_ZDBPEN     (0x0800) /* Enable ZD with ZDBPSELx - AGA */
-#define BPC2_ZDCTEN     (0x0400) /* Enable ZD with palette bit #31 - AGA */
-#define BPC2_KILLEHB    (0x0200) /* Kill EHB mode - AGA */
-#define BPC2_RDRAM      (0x0100) /* Color table accesses read, not write - AGA */
-#define BPC2_SOGEN      (0x0080) /* SOG output pin high - AGA */
-#define BPC2_PF2PRI     (0x0040) /* PF2 priority over PF1 */
-#define BPC2_PF2P2      (0x0020) /* PF2 priority wrt sprites */
-#define BPC2_PF2P1      (0x0010)
-#define BPC2_PF2P0      (0x0008)
-#define BPC2_PF1P2      (0x0004) /* ditto PF1 */
-#define BPC2_PF1P1      (0x0002)
-#define BPC2_PF1P0      (0x0001)
-
-
-   /*
-    *    BPLCON3 -- Bitplane Control Register 3 (AGA)
-    */
-
-#define BPC3_BANK2      (0x8000) /* Bits to select color register bank */
-#define BPC3_BANK1      (0x4000)
-#define BPC3_BANK0      (0x2000)
-#define BPC3_PF2OF2     (0x1000) /* Bits for color table offset when PF2 */
-#define BPC3_PF2OF1     (0x0800)
-#define BPC3_PF2OF0     (0x0400)
-#define BPC3_LOCT       (0x0200) /* Color register writes go to low bits */
-#define BPC3_SPRES1     (0x0080) /* Sprite resolution bits */
-#define BPC3_SPRES0     (0x0040)
-#define BPC3_BRDRBLNK   (0x0020) /* Border blanked? */
-#define BPC3_BRDRTRAN   (0x0010) /* Border transparent? */
-#define BPC3_ZDCLKEN    (0x0004) /* ZD pin is 14 MHz (HIRES) clock output */
-#define BPC3_BRDRSPRT   (0x0002) /* Sprites in border? */
-#define BPC3_EXTBLKEN   (0x0001) /* BLANK programmable */
-
-
-   /*
-    *    BPLCON4 -- Bitplane Control Register 4 (AGA)
-    */
-
-#define BPC4_BPLAM7     (0x8000) /* bitplane color XOR field */
-#define BPC4_BPLAM6     (0x4000)
-#define BPC4_BPLAM5     (0x2000)
-#define BPC4_BPLAM4     (0x1000)
-#define BPC4_BPLAM3     (0x0800)
-#define BPC4_BPLAM2     (0x0400)
-#define BPC4_BPLAM1     (0x0200)
-#define BPC4_BPLAM0     (0x0100)
-#define BPC4_ESPRM7     (0x0080) /* 4 high bits for even sprite colors */
-#define BPC4_ESPRM6     (0x0040)
-#define BPC4_ESPRM5     (0x0020)
-#define BPC4_ESPRM4     (0x0010)
-#define BPC4_OSPRM7     (0x0008) /* 4 high bits for odd sprite colors */
-#define BPC4_OSPRM6     (0x0004)
-#define BPC4_OSPRM5     (0x0002)
-#define BPC4_OSPRM4     (0x0001)
-
-
-   /*
-    *    BEAMCON0 -- Beam Control Register
-    */
-
-#define BMC0_HARDDIS    (0x4000) /* Disable hardware limits */
-#define BMC0_LPENDIS    (0x2000) /* Disable light pen latch */
-#define BMC0_VARVBEN    (0x1000) /* Enable variable vertical blank */
-#define BMC0_LOLDIS     (0x0800) /* Disable long/short line toggle */
-#define BMC0_CSCBEN     (0x0400) /* Composite sync/blank */
-#define BMC0_VARVSYEN   (0x0200) /* Enable variable vertical sync */
-#define BMC0_VARHSYEN   (0x0100) /* Enable variable horizontal sync */
-#define BMC0_VARBEAMEN  (0x0080) /* Enable variable beam counters */
-#define BMC0_DUAL       (0x0080) /* Enable alternate horizontal beam counter */
-#define BMC0_PAL        (0x0020) /* Set decodes for PAL */
-#define BMC0_VARCSYEN   (0x0010) /* Enable variable composite sync */
-#define BMC0_BLANKEN    (0x0008) /* Blank enable (no longer used on AGA) */
-#define BMC0_CSYTRUE    (0x0004) /* CSY polarity */
-#define BMC0_VSYTRUE    (0x0002) /* VSY polarity */
-#define BMC0_HSYTRUE    (0x0001) /* HSY polarity */
-
-
-   /*
-    *    FMODE -- Fetch Mode Control Register (AGA)
-    */
-
-#define FMODE_SSCAN2    (0x8000) /* Sprite scan-doubling */
-#define FMODE_BSCAN2    (0x4000) /* Use PF2 modulus every other line */
-#define FMODE_SPAGEM    (0x0008) /* Sprite page mode */
-#define FMODE_SPR32     (0x0004) /* Sprite 32 bit fetch */
-#define FMODE_BPAGEM    (0x0002) /* Bitplane page mode */
-#define FMODE_BPL32     (0x0001) /* Bitplane 32 bit fetch */
-
-
-   /*
-    *    Tags used to indicate a specific Pixel Clock
-    *
-    *    clk_shift is the shift value to get the timings in 35 ns units
-    */
-
-#define TAG_SHRES       (1)      /* SHRES, clk_shift = TAG_SHRES-1 */
-#define TAG_HIRES       (2)      /* HIRES, clk_shift = TAG_HIRES-1 */
-#define TAG_LORES       (3)      /* LORES, clk_shift = TAG_LORES-1 */
-
-
-   /*
-    *    Clock Definitions, Maximum Display Depth
-    *
-    *    These depend on the E-Clock or the Chipset, so they are filled in
-    *    dynamically
-    */
-
-static u_long pixclock[3];       /* SHRES/HIRES/LORES: index = clk_shift */
-static u_short maxdepth[3];      /* SHRES/HIRES/LORES: index = clk_shift */
-
-
-   /*
-    *    Broadcast Video Timings
-    *
-    *    Horizontal values are in 35 ns (SHRES) units
-    *    Vertical values are in non-interlaced scanlines
-    */
-
-#define PAL_WINDOW_H    (1472)   /* PAL Window Limits */
-#define PAL_WINDOW_V    (288)
-#define PAL_DIWSTRT_H   (360)
-#define PAL_DIWSTRT_V   (24)
-
-#define NTSC_WINDOW_H   (1472)   /* NTSC Window Limits */
-#define NTSC_WINDOW_V   (242)
-#define NTSC_DIWSTRT_H  (360)
-#define NTSC_DIWSTRT_V  (20)
-
-#define PAL_HTOTAL      (1816)   /* Total line length */
-#define NTSC_HTOTAL     (1816)   /* Needed for YWRAP */
-
-
-   /*
-    *    Monitor Specifications
-    *
-    *    These are typical for a `generic' Amiga monitor (e.g. A1960)
-    */
+#define BPC0_HIRES	(0x8000)
+#define BPC0_BPU2	(0x4000) /* Bit plane used count */
+#define BPC0_BPU1	(0x2000)
+#define BPC0_BPU0	(0x1000)
+#define BPC0_HAM	(0x0800) /* HAM mode */
+#define BPC0_DPF	(0x0400) /* Double playfield */
+#define BPC0_COLOR	(0x0200) /* Enable colorburst */
+#define BPC0_GAUD	(0x0100) /* Genlock audio enable */
+#define BPC0_UHRES	(0x0080) /* Ultrahi res enable */
+#define BPC0_SHRES	(0x0040) /* Super hi res mode */
+#define BPC0_BYPASS	(0x0020) /* Bypass LUT - AGA */
+#define BPC0_BPU3	(0x0010) /* AGA */
+#define BPC0_LPEN	(0x0008) /* Light pen enable */
+#define BPC0_LACE	(0x0004) /* Interlace */
+#define BPC0_ERSY	(0x0002) /* External resync */
+#define BPC0_ECSENA	(0x0001) /* ECS enable */
 
-static long vfmin = 50, vfmax = 90, hfmin = 15000, hfmax = 38000;
+	/*
+	 * BPLCON2 -- Bitplane Control Register 2
+	 */
 
-static u_short pwrsave = 0;      /* VESA suspend mode (not for PAL/NTSC) */
+#define BPC2_ZDBPSEL2	(0x4000) /* Bitplane to be used for ZD - AGA */
+#define BPC2_ZDBPSEL1	(0x2000)
+#define BPC2_ZDBPSEL0	(0x1000)
+#define BPC2_ZDBPEN	(0x0800) /* Enable ZD with ZDBPSELx - AGA */
+#define BPC2_ZDCTEN	(0x0400) /* Enable ZD with palette bit #31 - AGA */
+#define BPC2_KILLEHB	(0x0200) /* Kill EHB mode - AGA */
+#define BPC2_RDRAM	(0x0100) /* Color table accesses read, not write - AGA */
+#define BPC2_SOGEN	(0x0080) /* SOG output pin high - AGA */
+#define BPC2_PF2PRI	(0x0040) /* PF2 priority over PF1 */
+#define BPC2_PF2P2	(0x0020) /* PF2 priority wrt sprites */
+#define BPC2_PF2P1	(0x0010)
+#define BPC2_PF2P0	(0x0008)
+#define BPC2_PF1P2	(0x0004) /* ditto PF1 */
+#define BPC2_PF1P1	(0x0002)
+#define BPC2_PF1P0	(0x0001)
 
+	/*
+	 * BPLCON3 -- Bitplane Control Register 3 (AGA)
+	 */
 
-   /*
-    *    Various macros
-    */
+#define BPC3_BANK2	(0x8000) /* Bits to select color register bank */
+#define BPC3_BANK1	(0x4000)
+#define BPC3_BANK0	(0x2000)
+#define BPC3_PF2OF2	(0x1000) /* Bits for color table offset when PF2 */
+#define BPC3_PF2OF1	(0x0800)
+#define BPC3_PF2OF0	(0x0400)
+#define BPC3_LOCT	(0x0200) /* Color register writes go to low bits */
+#define BPC3_SPRES1	(0x0080) /* Sprite resolution bits */
+#define BPC3_SPRES0	(0x0040)
+#define BPC3_BRDRBLNK	(0x0020) /* Border blanked? */
+#define BPC3_BRDRTRAN	(0x0010) /* Border transparent? */
+#define BPC3_ZDCLKEN	(0x0004) /* ZD pin is 14 MHz (HIRES) clock output */
+#define BPC3_BRDRSPRT	(0x0002) /* Sprites in border? */
+#define BPC3_EXTBLKEN	(0x0001) /* BLANK programmable */
 
-#define up8(x)          (((x)+7) & ~7)
-#define down8(x)        ((x) & ~7)
-#define div8(x)         ((x)>>3)
-#define mod8(x)         ((x) & 7)
+	/*
+	 * BPLCON4 -- Bitplane Control Register 4 (AGA)
+	 */
 
-#define up16(x)         (((x)+15) & ~15)
-#define down16(x)       ((x) & ~15)
-#define div16(x)        ((x)>>4)
-#define mod16(x)        ((x) & 15)
+#define BPC4_BPLAM7	(0x8000) /* bitplane color XOR field */
+#define BPC4_BPLAM6	(0x4000)
+#define BPC4_BPLAM5	(0x2000)
+#define BPC4_BPLAM4	(0x1000)
+#define BPC4_BPLAM3	(0x0800)
+#define BPC4_BPLAM2	(0x0400)
+#define BPC4_BPLAM1	(0x0200)
+#define BPC4_BPLAM0	(0x0100)
+#define BPC4_ESPRM7	(0x0080) /* 4 high bits for even sprite colors */
+#define BPC4_ESPRM6	(0x0040)
+#define BPC4_ESPRM5	(0x0020)
+#define BPC4_ESPRM4	(0x0010)
+#define BPC4_OSPRM7	(0x0008) /* 4 high bits for odd sprite colors */
+#define BPC4_OSPRM6	(0x0004)
+#define BPC4_OSPRM5	(0x0002)
+#define BPC4_OSPRM4	(0x0001)
 
-#define up32(x)         (((x)+31) & ~31)
-#define down32(x)       ((x) & ~31)
-#define div32(x)        ((x)>>5)
-#define mod32(x)        ((x) & 31)
+	/*
+	 * BEAMCON0 -- Beam Control Register
+	 */
 
-#define up64(x)         (((x)+63) & ~63)
-#define down64(x)       ((x) & ~63)
-#define div64(x)        ((x)>>6)
-#define mod64(x)        ((x) & 63)
+#define BMC0_HARDDIS	(0x4000) /* Disable hardware limits */
+#define BMC0_LPENDIS	(0x2000) /* Disable light pen latch */
+#define BMC0_VARVBEN	(0x1000) /* Enable variable vertical blank */
+#define BMC0_LOLDIS	(0x0800) /* Disable long/short line toggle */
+#define BMC0_CSCBEN	(0x0400) /* Composite sync/blank */
+#define BMC0_VARVSYEN	(0x0200) /* Enable variable vertical sync */
+#define BMC0_VARHSYEN	(0x0100) /* Enable variable horizontal sync */
+#define BMC0_VARBEAMEN	(0x0080) /* Enable variable beam counters */
+#define BMC0_DUAL	(0x0040) /* Enable alternate horizontal beam counter */
+#define BMC0_PAL	(0x0020) /* Set decodes for PAL */
+#define BMC0_VARCSYEN	(0x0010) /* Enable variable composite sync */
+#define BMC0_BLANKEN	(0x0008) /* Blank enable (no longer used on AGA) */
+#define BMC0_CSYTRUE	(0x0004) /* CSY polarity */
+#define BMC0_VSYTRUE	(0x0002) /* VSY polarity */
+#define BMC0_HSYTRUE	(0x0001) /* HSY polarity */
 
-#define min(a, b)       ((a) < (b) ? (a) : (b))
-#define max(a, b)       ((a) > (b) ? (a) : (b))
 
-#define highw(x)        ((u_long)(x)>>16 & 0xffff)
-#define loww(x)         ((u_long)(x) & 0xffff)
+	/*
+	 * FMODE -- Fetch Mode Control Register (AGA)
+	 */
 
-#define arraysize(x)    (sizeof(x)/sizeof(*(x)))
+#define FMODE_SSCAN2	(0x8000) /* Sprite scan-doubling */
+#define FMODE_BSCAN2	(0x4000) /* Use PF2 modulus every other line */
+#define FMODE_SPAGEM	(0x0008) /* Sprite page mode */
+#define FMODE_SPR32	(0x0004) /* Sprite 32 bit fetch */
+#define FMODE_BPAGEM	(0x0002) /* Bitplane page mode */
+#define FMODE_BPL32	(0x0001) /* Bitplane 32 bit fetch */
 
+	/*
+	 * Tags used to indicate a specific Pixel Clock
+	 *
+	 * clk_shift is the shift value to get the timings in 35 ns units
+	 */
 
-   /*
-    *    Chip RAM we reserve for the Frame Buffer (must be a multiple of 4K!)
-    *
-    *    This defines the Maximum Virtual Screen Size
-    */
+enum { TAG_SHRES, TAG_HIRES, TAG_LORES };
 
-#define VIDEOMEMSIZE_AGA_2M   (1310720)   /* AGA (2MB) : max 1280*1024*256 */
-#define VIDEOMEMSIZE_AGA_1M    (393216)   /* AGA (1MB) : max 1024*768*256 */
-#define VIDEOMEMSIZE_ECS_2M    (655360)   /* ECS (2MB) : max 1280*1024*16 */
-#define VIDEOMEMSIZE_ECS_1M    (393216)   /* ECS (1MB) : max 1024*768*16 */
-#define VIDEOMEMSIZE_OCS       (262144)   /* OCS       : max ca. 800*600*16 */
+	/*
+	 * Tags used to indicate the specific chipset
+	 */
 
+enum { TAG_OCS, TAG_ECS, TAG_AGA };
 
-static u_long videomemory;
-static u_long videomemorysize;
+	/*
+	 * Tags used to indicate the memory bandwidth
+	 */
 
-#define assignchunk(name, type, ptr, size) \
-{ \
-   (name) = (type)(ptr); \
-   ptr += size; \
-}
+enum { TAG_FMODE_1, TAG_FMODE_2, TAG_FMODE_4 };
 
 
-   /*
-    *    Copper Instructions
-    */
+	/*
+	 * Clock Definitions, Maximum Display Depth
+	 *
+	 * These depend on the E-Clock or the Chipset, so they are filled in
+	 * dynamically
+	 */
 
-#define CMOVE(val, reg)       (CUSTOM_OFS(reg)<<16 | (val))
-#define CMOVE2(val, reg)      ((CUSTOM_OFS(reg)+2)<<16 | (val))
-#define CWAIT(x, y)           (((y) & 0xff)<<24 | ((x) & 0xfe)<<16 | 0x0001fffe)
-#define CEND                  (0xfffffffe)
+static u_long pixclock[3];	/* SHRES/HIRES/LORES: index = clk_shift */
+static u_short maxdepth[3];	/* SHRES/HIRES/LORES: index = clk_shift */
+static u_short maxfmode, chipset;
 
 
-typedef union {
-   u_long l;
-   u_short w[2];
-} copins;
+	/*
+	 * Broadcast Video Timings
+	 *
+	 * Horizontal values are in 35 ns (SHRES) units
+	 * Vertical values are in interlaced scanlines
+	 */
 
+#define PAL_DIWSTRT_H	(360)	/* PAL Window Limits */
+#define PAL_DIWSTRT_V	(48)
+#define PAL_HTOTAL	(1816)
+#define PAL_VTOTAL	(625)
+
+#define NTSC_DIWSTRT_H	(360)	/* NTSC Window Limits */
+#define NTSC_DIWSTRT_V	(40)
+#define NTSC_HTOTAL	(1816)
+#define NTSC_VTOTAL	(525)
 
-   /*
-    *    Frame Header Copper List
-    */
-
-struct clist_hdr {
-   copins bplcon0;
-   copins diwstrt;
-   copins diwstop;
-   copins diwhigh;
-   copins sprfix[8];
-   copins sprstrtup[16];
-   copins wait;
-   copins jump;
-   copins wait_forever;
-};
 
+	/*
+	 * Monitor Specifications
+	 *
+	 * These are typical for a `generic' Amiga monitor (e.g. A1960)
+	 */
 
-   /*
-    *    Long Frame/Short Frame Copper List
-    */
-
-struct clist_dyn {
-   copins diwstrt;
-   copins diwstop;
-   copins diwhigh;
-   copins bplcon0;
-   copins sprpt[2];              /* Sprite 0 */
-   copins rest[64];
-};
+static long vfmin = 50, vfmax = 90, hfmin = 15000, hfmax = 38000;
 
 
-static struct clist_hdr *clist_hdr;
-static struct clist_dyn *clist_lof;
-static struct clist_dyn *clist_shf;    /* Only used for Interlace */
+	/*
+	 * Various macros
+	 */
 
+#define up2(v)		(((v)+1) & -2)
+#define down2(v)	((v) & -2)
+#define div2(v)		((v)>>1)
+#define mod2(v)		((v) & 1)
+
+#define up4(v)		(((v)+3) & -4)
+#define down4(v)	((v) & -4)
+#define mul4(v)		((v)<<2)
+#define div4(v)		((v)>>2)
+#define mod4(v)		((v) & 3)
+
+#define up8(v)		(((v)+7) & -8)
+#define down8(v)	((v) & -8)
+#define div8(v)		((v)>>3)
+#define mod8(v)		((v) & 7)
+
+#define up16(v)		(((v)+15) & -16)
+#define down16(v)	((v) & -16)
+#define div16(v)	((v)>>4)
+#define mod16(v)	((v) & 15)
+
+#define up32(v)		(((v)+31) & -32)
+#define down32(v)	((v) & -32)
+#define div32(v)	((v)>>5)
+#define mod32(v)	((v) & 31)
+
+#define up64(v)		(((v)+63) & -64)
+#define down64(v)	((v) & -64)
+#define div64(v)	((v)>>6)
+#define mod64(v)	((v) & 63)
+
+#define upx(x,v)	(((v)+(x)-1) & -(x))
+#define downx(x,v)	((v) & -(x))
+#define modx(x,v)	((v) & ((x)-1))
+
+/* if x1 is not a constant, this macro won't make real sense :-) */
+#define DIVUL(x1, x2) ({int res; asm("divul %1,%2,%3": "=d" (res): \
+	"d" (x2), "d" ((long)((x1)/0x100000000ULL)), "0" ((long)(x1))); res;})
+
+#define min(a, b)	((a) < (b) ? (a) : (b))
+#define max(a, b)	((a) > (b) ? (a) : (b))
 
-   /*
-    *    Hardware Cursor
-    */
+#define highw(x)	((u_long)(x)>>16 & 0xffff)
+#define loww(x)		((u_long)(x) & 0xffff)
 
-#define CRSR_RATE       (20)     /* Number of frames/flash toggle */
+#define arraysize(x)	(sizeof(x)/sizeof(*(x)))
 
-static u_long *lofsprite, *shfsprite, *dummysprite;
-static u_short cursormode = FB_CURSOR_FLASH;
+#define VBlankOn()	custom.intena = IF_SETCLR|IF_COPER
+#define VBlankOff()	custom.intena = IF_COPER
 
 
-   /*
-    *    Current Video Mode
-    */
+	/*
+	 * Chip RAM we reserve for the Frame Buffer
+	 *
+	 * This defines the Maximum Virtual Screen Size
+	 * (Setable per kernel options?)
+	 */
 
-struct amiga_fb_par {
+#define VIDEOMEMSIZE_AGA_2M	(1310720) /* AGA (2MB) : max 1280*1024*256  */
+#define VIDEOMEMSIZE_AGA_1M	(786432)  /* AGA (1MB) : max 1024*768*256   */
+#define VIDEOMEMSIZE_ECS_2M	(655360)  /* ECS (2MB) : max 1280*1024*16   */
+#define VIDEOMEMSIZE_ECS_1M	(393216)  /* ECS (1MB) : max 1024*768*16    */
+#define VIDEOMEMSIZE_OCS	(262144)  /* OCS       : max ca. 800*600*16 */
 
-   /* General Values */
+#define SPRITEMEMSIZE		(64*64/4) /* max 64*64*4 */
+#define DUMMYSPRITEMEMSIZE	(8)
 
-   int xres;                     /* vmode */
-   int yres;                     /* vmode */
-   int vxres;                    /* vmode */
-   int vyres;                    /* vmode */
-   int xoffset;                  /* vmode */
-   int yoffset;                  /* vmode */
-   u_short bpp;                  /* vmode */
-   u_short clk_shift;            /* vmode */
-   int vmode;                    /* vmode */
-   u_short diwstrt_h;            /* vmode */
-   u_short diwstrt_v;            /* vmode */
-   u_long next_line;             /* modulo for next line */
-   u_long next_plane;            /* modulo for next plane */
-   short crsr_x;                 /* movecursor */
-   short crsr_y;                 /* movecursor */
-
-   /* OCS Hardware Registers */
-
-   u_long bplpt0;                /* vmode, pan (Note: physical address) */
-   u_short bplcon0;              /* vmode */
-   u_short bplcon1;              /* vmode, pan */
-   u_short bpl1mod;              /* vmode, pan */
-   u_short bpl2mod;              /* vmode, pan */
-   u_short diwstrt;              /* vmode */
-   u_short diwstop;              /* vmode */
-   u_short ddfstrt;              /* vmode, pan */
-   u_short ddfstop;              /* vmode, pan */
-
-#if defined(CONFIG_AMIFB_ECS) || defined(CONFIG_AMIFB_AGA)
-   /* Additional ECS Hardware Registers */
-
-   u_short diwhigh;              /* vmode */
-   u_short bplcon3;              /* vmode */
-   u_short beamcon0;             /* vmode */
-   u_short htotal;               /* vmode */
-   u_short hsstrt;               /* vmode */
-   u_short hsstop;               /* vmode */
-   u_short vtotal;               /* vmode */
-   u_short vsstrt;               /* vmode */
-   u_short vsstop;               /* vmode */
-   u_short hcenter;              /* vmode */
-#endif /* defined(CONFIG_AMIFB_ECS) || defined(CONFIG_AMIFB_AGA) */
+static u_long videomemory, spritememory;
+static u_long videomemorysize;
 
-#if defined(CONFIG_AMIFB_AGA)
-   /* Additional AGA Hardware Registers */
+	/*
+	 * This is the earliest allowed start of fetching display data.
+	 * Only if you really want no hardware cursor and audio,
+	 * set this to 128, but let it better at 192
+	 */
 
-   u_short fmode;                /* vmode */
-#endif /* defined(CONFIG_AMIFB_AGA) */
-};
+static u_long min_fstrt = 192;
 
-static struct amiga_fb_par current_par;
+#define assignchunk(name, type, ptr, size) \
+{ \
+	(name) = (type)(ptr); \
+	ptr += size; \
+}
 
-static int current_par_valid = 0;
-static int currcon = 0;
 
-static struct display disp[MAX_NR_CONSOLES];
-static struct fb_info fb_info;
+	/*
+	 * Copper Instructions
+	 */
 
-static int node;        /* node of the /dev/fb?current file */
+#define CMOVE(val, reg)		(CUSTOM_OFS(reg)<<16 | (val))
+#define CMOVE2(val, reg)	((CUSTOM_OFS(reg)+2)<<16 | (val))
+#define CWAIT(x, y)		(((y) & 0x1fe)<<23 | ((x) & 0x7f0)<<13 | 0x0001fffe)
+#define CEND			(0xfffffffe)
 
 
-   /*
-    *    The minimum period for audio depends on htotal (for OCS/ECS/AGA)
-    *    (Imported from arch/m68k/amiga/amisound.c)
-    */
+typedef union {
+	u_long l;
+	u_short w[2];
+} copins;
 
-extern volatile u_short amiga_audio_min_period;
+struct copdisplay {
+	copins *init;
+	copins *wait;
+	copins *list[2][2];
+	copins *rebuild[2];
+} copdisplay;
 
+static u_short currentcop = 0;
 
-   /*
-    *    Since we can't read the palette on OCS/ECS, and since reading one
-    *    single color palette entry requires 5 expensive custom chip bus
-    *    accesses on AGA, we keep a copy of the current palette.
-    */
+	/*
+	 * Hardware Cursor
+	 */
 
-#ifdef CONFIG_AMIFB_AGA
-static struct { u_char red, green, blue, pad; } palette[256];
-#else /* CONFIG_AMIFB_AGA */
-static struct { u_char red, green, blue, pad; } palette[32];
-#endif /* CONFIG_AMIFB_AGA */
+static int cursorrate = 20;	/* Number of frames/flash toggle */
+static u_short cursorstate = -1;
+static u_short cursormode = FB_CURSOR_OFF;
 
+static u_short *lofsprite, *shfsprite, *dummysprite;
 
-   /*
-    *    Latches and Flags for display changes during VBlank
-    */
-
-static volatile u_short do_vmode = 0;           /* Change the Video Mode */
-static volatile short do_blank = 0;             /* (Un)Blank the Screen (±1) */
-static volatile u_short do_movecursor = 0;      /* Move the Cursor */
-static volatile u_short full_vmode_change = 1;  /* Full Change or Only Pan */
-static volatile u_short is_blanked = 0;         /* Screen is Blanked */
-
-
-   /*
-    *    Switch for Chipset Independency
-    */
-
-static struct fb_hwswitch {
-
-   /* Initialization */
-   int (*init)(void);
-
-   /* Display Control */
-   int (*encode_fix)(struct fb_fix_screeninfo *fix, struct amiga_fb_par *par);
-   int (*decode_var)(struct fb_var_screeninfo *var, struct amiga_fb_par *par);
-   int (*encode_var)(struct fb_var_screeninfo *var, struct amiga_fb_par *par);
-   int (*getcolreg)(u_int regno, u_int *red, u_int *green, u_int *blue,
-                    u_int *transp);
-   int (*setcolreg)(u_int regno, u_int red, u_int green, u_int blue,
-                    u_int transp);
-   int (*pan_display)(struct fb_var_screeninfo *var, struct amiga_fb_par *par);
-
-   /* Routines Called by VBlank Interrupt to minimize flicker */
-   void (*do_vmode)(void);
-   void (*do_blank)(int blank);
-   void (*do_movecursor)(void);
-   void (*do_flashcursor)(void);
-} *fbhw;
-
-
-   /*
-    *    Frame Buffer Name
-    *
-    *    The rest of the name is filled in by amiga_fb_init
-    */
+	/*
+	 * Current Video Mode
+	 */
 
-static char amiga_fb_name[16] = "Amiga ";
+struct amiga_fb_par {
 
+	/* General Values */
 
-   /*
-    *    Predefined Video Mode Names
-    *
-    *    The a2024-?? modes don't work yet because there's no A2024 driver.
-    */
+	int xres;		/* vmode */
+	int yres;		/* vmode */
+	int vxres;		/* vmode */
+	int vyres;		/* vmode */
+	int xoffset;		/* vmode */
+	int yoffset;		/* vmode */
+	u_short bpp;		/* vmode */
+	u_short clk_shift;	/* vmode */
+	u_short line_shift;	/* vmode */
+	int vmode;		/* vmode */
+	u_short diwstrt_h;	/* vmode */
+	u_short diwstop_h;	/* vmode */
+	u_short diwstrt_v;	/* vmode */
+	u_short diwstop_v;	/* vmode */
+	u_long next_line;	/* modulo for next line */
+	u_long next_plane;	/* modulo for next plane */
+
+	/* Cursor Values */
+
+	struct {
+		short crsr_x;	/* movecursor */
+		short crsr_y;	/* movecursor */
+		short spot_x;
+		short spot_y;
+		u_short height;
+		u_short width;
+		u_short fmode;
+	} crsr;
+
+	/* OCS Hardware Registers */
+
+	u_long bplpt0;		/* vmode, pan (Note: physical address) */
+	u_long bplpt0wrap;	/* vmode, pan (Note: physical address) */
+	u_short ddfstrt;
+	u_short ddfstop;
+	u_short bpl1mod;
+	u_short bpl2mod;
+	u_short bplcon0;	/* vmode */
+	u_short bplcon1;	/* vmode */
+	u_short htotal;		/* vmode */
+	u_short vtotal;		/* vmode */
+
+	/* Additional ECS Hardware Registers */
+
+	u_short bplcon3;	/* vmode */
+	u_short beamcon0;	/* vmode */
+	u_short hsstrt;		/* vmode */
+	u_short hsstop;		/* vmode */
+	u_short hbstrt;		/* vmode */
+	u_short hbstop;		/* vmode */
+	u_short vsstrt;		/* vmode */
+	u_short vsstop;		/* vmode */
+	u_short vbstrt;		/* vmode */
+	u_short vbstop;		/* vmode */
+	u_short hcenter;	/* vmode */
 
-static char *amiga_fb_modenames[] = {
+	/* Additional AGA Hardware Registers */
 
-   /*
-    *    Autodetect (Default) Video Mode
-    */
-
-   "default",
-
-   /*
-    *    AmigaOS Video Modes
-    */
-    
-   "ntsc",              /* 640x200, 15 kHz, 60 Hz (NTSC) */
-   "ntsc-lace",         /* 640x400, 15 kHz, 60 Hz interlaced (NTSC) */
-   "pal",               /* 640x256, 15 kHz, 50 Hz (PAL) */
-   "pal-lace",          /* 640x512, 15 kHz, 50 Hz interlaced (PAL) */
-   "multiscan",         /* 640x480, 29 kHz, 57 Hz */
-   "multiscan-lace",    /* 640x960, 29 kHz, 57 Hz interlaced */
-   "a2024-10",          /* 1024x800, 10 Hz (Not yet supported) */
-   "a2024-15",          /* 1024x800, 15 Hz (Not yet supported) */
-   "euro36",            /* 640x200, 15 kHz, 72 Hz */
-   "euro36-lace",       /* 640x400, 15 kHz, 72 Hz interlaced */
-   "euro72",            /* 640x400, 29 kHz, 68 Hz */
-   "euro72-lace",       /* 640x800, 29 kHz, 68 Hz interlaced */
-   "super72",           /* 800x300, 23 kHz, 70 Hz */
-   "super72-lace",      /* 800x600, 23 kHz, 70 Hz interlaced */
-   "dblntsc",           /* 640x200, 27 kHz, 57 Hz doublescan */
-   "dblntsc-ff",        /* 640x400, 27 kHz, 57 Hz */
-   "dblntsc-lace",      /* 640x800, 27 kHz, 57 Hz interlaced */
-   "dblpal",            /* 640x256, 27 kHz, 47 Hz doublescan */
-   "dblpal-ff",         /* 640x512, 27 kHz, 47 Hz */
-   "dblpal-lace",       /* 640x1024, 27 kHz, 47 Hz interlaced */
-
-   /*
-    *    VGA Video Modes
-    */
-
-   "vga",               /* 640x480, 31 kHz, 60 Hz (VGA) */
-   "vga70",             /* 640x400, 31 kHz, 70 Hz (VGA) */
-
-   /*
-    *    User Defined Video Modes: to be set after boot up using e.g. fbset
-    */
+	u_short fmode;		/* vmode */
+} currentpar;
 
-   "user0", "user1", "user2", "user3", "user4", "user5", "user6", "user7"
-};
+static int currcon = 0;
 
+static struct display disp[MAX_NR_CONSOLES];
+static struct fb_info fb_info;
 
-   /*
-    *    Predefined Video Mode Definitions
-    *
-    *    Since the actual pixclock values depend on the E-Clock, we use the
-    *    TAG_* values and fill in the real values during initialization.
-    *    Thus we assume no one has pixel clocks of 333, 500 or 1000 GHz :-)
-    */
-
-static struct fb_var_screeninfo amiga_fb_predefined[] = {
-
-   /*
-    *    Autodetect (Default) Video Mode
-    */
-
-   { 0, },
-
-   /*
-    *    AmigaOS Video Modes
-    */
-    
-   {
-      /* ntsc */
-      640, 200, 640, 200, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 78, 18, 24, 18, 0, 0,
-      FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
-   }, {
-      /* ntsc-lace */
-      640, 400, 640, 400, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 78, 18, 48, 36, 0, 0,
-      FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
-   }, {
-      /* pal */
-      640, 256, 640, 256, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 78, 18, 20, 12, 0, 0,
-      FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED
-   }, {
-      /* pal-lace */
-      640, 512, 640, 512, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 78, 18, 40, 24, 0, 0,
-      FB_SYNC_BROADCAST, FB_VMODE_INTERLACED
-   }, {
-      /* multiscan */
-      640, 480, 640, 480, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 164, 92, 9, 9, 80, 8,
-      0, FB_VMODE_NONINTERLACED
-   }, {
-      /* multiscan-lace */
-      640, 960, 640, 960, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 164, 92, 18, 18, 80, 16,
-      0, FB_VMODE_INTERLACED
-   }, {
-      /* a2024-10 (Not yet supported) */
-      1024, 800, 1024, 800, 0, 0, 2, 0,
-      {0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 0, 0, 0, 0, 0, 0,
-      0, FB_VMODE_NONINTERLACED
-   }, {
-      /* a2024-15 (Not yet supported) */
-      1024, 800, 1024, 800, 0, 0, 2, 0,
-      {0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 0, 0, 0, 0, 0, 0,
-      0, FB_VMODE_NONINTERLACED
-   }, {
-      /* euro36 */
-      640, 200, 640, 200, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 92, 124, 6, 6, 52, 5,
-      0, FB_VMODE_NONINTERLACED
-   }, {
-      /* euro36-lace */
-      640, 400, 640, 400, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 92, 124, 12, 12, 52, 10,
-      0, FB_VMODE_INTERLACED
-   }, {
-      /* euro72 */
-      640, 400, 640, 400, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 164, 92, 9, 9, 80, 8,
-      0, FB_VMODE_NONINTERLACED
-   }, {
-      /* euro72-lace */
-      640, 800, 640, 800, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 164, 92, 18, 18, 80, 16,
-      0, FB_VMODE_INTERLACED
-   }, {
-      /* super72 */
-      800, 300, 800, 300, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 212, 140, 10, 11, 80, 7,
-      0, FB_VMODE_NONINTERLACED
-   }, {
-      /* super72-lace */
-      800, 600, 800, 600, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 212, 140, 20, 22, 80, 14,
-      0, FB_VMODE_INTERLACED
-   }, {
-      /* dblntsc */
-      640, 200, 640, 200, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 18, 17, 80, 4,
-      0, FB_VMODE_DOUBLE
-   }, {
-      /* dblntsc-ff */
-      640, 400, 640, 400, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 36, 35, 80, 7,
-      0, FB_VMODE_NONINTERLACED
-   }, {
-      /* dblntsc-lace */
-      640, 800, 640, 800, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 72, 70, 80, 14,
-      0, FB_VMODE_INTERLACED
-   }, {
-      /* dblpal */
-      640, 256, 640, 256, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 14, 13, 80, 4,
-      0, FB_VMODE_DOUBLE
-   }, {
-      /* dblpal-ff */
-      640, 512, 640, 512, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 28, 27, 80, 7,
-      0, FB_VMODE_NONINTERLACED
-   }, {
-      /* dblpal-lace */
-      640, 1024, 640, 1024, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 56, 54, 80, 14,
-      0, FB_VMODE_INTERLACED
-   },
-
-   /*
-    *    VGA Video Modes
-    */
-
-   {
-      /* vga */
-      640, 480, 640, 480, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 64, 96, 30, 9, 112, 2,
-      0, FB_VMODE_NONINTERLACED
-   }, {
-      /* vga70 */
-      640, 400, 640, 400, 0, 0, 4, 0,
-      {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
-      0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 64, 96, 35, 12, 112, 2,
-      FB_SYNC_VERT_HIGH_ACT | FB_SYNC_COMP_HIGH_ACT, FB_VMODE_NONINTERLACED
-   },
-
-   /*
-    *    User Defined Video Modes
-    */
+static int node;		/* node of the /dev/fb?current file */
 
-   { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }
-};
+	/*
+	 * The minimum period for audio depends on htotal (for OCS/ECS/AGA)
+	 * (Imported from arch/m68k/amiga/amisound.c)
+	 */
 
+extern volatile u_short amiga_audio_min_period;
 
-#define NUM_USER_MODES     (8)
-#define NUM_TOTAL_MODES    arraysize(amiga_fb_predefined)
-#define NUM_PREDEF_MODES   (NUM_TOTAL_MODES-NUM_USER_MODES)
+	/*
+	 * Since we can't read the palette on OCS/ECS, and since reading one
+	 * single color palette entry require 5 expensive custom chip bus accesses
+	 * on AGA, we keep a copy of the current palette.
+	 */
 
+#if defined(CONFIG_AMIFB_AGA)
+static struct { u_char red, green, blue, pad; } palette[256];
+#else
+static struct { u_char red, green, blue, pad; } palette[32];
+#endif
 
-static int amifb_ilbm = 0;       /* interleaved or normal bitplanes */
+#if defined(CONFIG_AMIFB_ECS)
+static u_short ecs_palette[32];
+#endif
 
-static int amifb_inverse = 0;
-static int amifb_mode = 0;
+	/*
+	 * Latches for Display Changes during VBlank
+	 */
 
+static u_short do_vmode_full = 0;	/* Change the Video Mode */
+static u_short do_vmode_pan = 0;	/* Update the Video Mode */
+static short do_blank = 0;		/* (Un)Blank the Screen (±1) */
+static u_short do_cursor = 0;		/* Move the Cursor */
 
-   /*
-    *    Support for Graphics Boards
-    */
 
-#ifdef CONFIG_FB_CYBER        /* Cybervision */
-extern int Cyber_probe(void);
-extern void Cyber_video_setup(char *options, int *ints);
-extern struct fb_info *Cyber_fb_init(long *mem_start);
+	/*
+	 * Various Flags
+	 */
 
-static int amifb_Cyber = 0;
-#endif /* CONFIG_FB_CYBER */
+static u_short is_blanked = 0;		/* Screen is Blanked */
+static u_short is_lace = 0;		/* Screen is laced */
 
+	/*
+	 * Frame Buffer Name
+	 *
+	 * The rest of the name is filled in during initialization
+	 */
 
-   /*
-    *    Some default modes
-    */
-
-#define DEFMODE_PAL        "pal"       /* for PAL OCS/ECS */
-#define DEFMODE_NTSC       "ntsc"      /* for NTSC OCS/ECS */
-#define DEFMODE_AMBER_PAL  "pal-lace"  /* for flicker fixed PAL (A3000) */
-#define DEFMODE_AMBER_NTSC "ntsc-lace" /* for flicker fixed NTSC (A3000) */
-#define DEFMODE_AGA        "vga70"     /* for AGA */
+static char amiga_fb_name[16] = "Amiga ";
 
+	/*
+	 * Predefined Video Mode Names
+	 *
+	 * The a2024-?? modes don't work yet because there's no A2024 driver.
+	 */
 
-   /*
-    *    Interface used by the world
-    */
+static char *amiga_fb_modenames[] = {
 
-void amiga_video_setup(char *options, int *ints);
+	/*
+	 * Autodetect (Default) Video Mode
+	 */
 
-static int amiga_fb_get_fix(struct fb_fix_screeninfo *fix, int con);
-static int amiga_fb_get_var(struct fb_var_screeninfo *var, int con);
-static int amiga_fb_set_var(struct fb_var_screeninfo *var, int con);
-static int amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con);
-static int amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con);
-static int amiga_fb_pan_display(struct fb_var_screeninfo *var, int con);
+	"default",
 
-static int amiga_fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
-                          u_long arg, int con);
+	/*
+	 * AmigaOS Video Modes
+	 */
 
-static int amiga_fb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con);
-static int amiga_fb_get_var_cursorinfo(struct fb_var_cursorinfo *var, int con);
-static int amiga_fb_set_var_cursorinfo(struct fb_var_cursorinfo *var, int con);
-static int amiga_fb_get_cursorstate(struct fb_cursorstate *state, int con);
-static int amiga_fb_set_cursorstate(struct fb_cursorstate *state, int con);
+	"ntsc",			/* 640x200, 15 kHz, 60 Hz (NTSC) */
+	"ntsc-lace",		/* 640x400, 15 kHz, 60 Hz interlaced (NTSC) */
+	"pal",			/* 640x256, 15 kHz, 50 Hz (PAL) */
+	"pal-lace",		/* 640x512, 15 kHz, 50 Hz interlaced (PAL) */
+	"multiscan",		/* 640x480, 29 kHz, 57 Hz */
+	"multiscan-lace",	/* 640x960, 29 kHz, 57 Hz interlaced */
+	"a2024-10",		/* 1024x800, 10 Hz (Not yet supported) */
+	"a2024-15",		/* 1024x800, 15 Hz (Not yet supported) */
+	"euro36",		/* 640x200, 15 kHz, 72 Hz */
+	"euro36-lace",		/* 640x400, 15 kHz, 72 Hz interlaced */
+	"euro72",		/* 640x400, 29 kHz, 68 Hz */
+	"euro72-lace",		/* 640x800, 29 kHz, 68 Hz interlaced */
+	"super72",		/* 800x300, 23 kHz, 70 Hz */
+	"super72-lace",		/* 800x600, 23 kHz, 70 Hz interlaced */
+	"dblntsc",		/* 640x200, 27 kHz, 57 Hz doublescan */
+	"dblntsc-ff",		/* 640x400, 27 kHz, 57 Hz */
+	"dblntsc-lace",		/* 640x800, 27 kHz, 57 Hz interlaced */
+	"dblpal",		/* 640x256, 27 kHz, 47 Hz doublescan */
+	"dblpal-ff",		/* 640x512, 27 kHz, 47 Hz */
+	"dblpal-lace",		/* 640x1024, 27 kHz, 47 Hz interlaced */
 
+	/*
+	 * VGA Video Modes
+	 */
 
-   /*
-    *    Interface to the low level console driver
-    */
+	"vga",			/* 640x480, 31 kHz, 60 Hz (VGA) */
+	"vga70",		/* 640x400, 31 kHz, 70 Hz (VGA) */
 
-struct fb_info *amiga_fb_init(long *mem_start);
-static int amifb_switch(int con);
-static int amifb_updatevar(int con);
-static void amifb_blank(int blank);
+	/*
+	 * User Defined Video Modes: to be set after boot up using e.g. fbset
+	 */
 
+	"user0", "user1", "user2", "user3", "user4", "user5", "user6", "user7"
+};
 
-   /*
-    *    Support for OCS
-    */
+struct fb_var_screeninfo amiga_fb_predefined[] = {
 
-#ifdef CONFIG_AMIFB_OCS
-#error "OCS support: not yet implemented"
-#endif /* CONFIG_AMIFB_OCS */
+	/*
+	 * Autodetect (Default) Video Mode
+	 */
 
+	{ 0, },
 
-   /*
-    *    Support for ECS
-    */
+	/*
+	 * AmigaOS Video Modes
+	 */
 
-#ifdef CONFIG_AMIFB_ECS
-#error "ECS support: not yet implemented"
-#endif /* CONFIG_AMIFB_ECS */
+	{
+		/* ntsc */
+		640, 200, 640, 200, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 44, 16, 76, 2,
+		FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* ntsc-lace */
+		640, 400, 640, 400, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 88, 33, 76, 4,
+		FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* pal */
+		640, 256, 640, 256, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 40, 14, 76, 2,
+		FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* pal-lace */
+		640, 512, 640, 512, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 106, 86, 80, 29, 76, 4,
+		FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* multiscan */
+		640, 480, 640, 480, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 96, 112, 29, 8, 72, 8,
+		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
 
+	}, {
+		/* multiscan-lace */
+		640, 960, 640, 960, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 96, 112, 58, 16, 72, 16,
+		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* a2024-10 (Not yet supported) */
+		1024, 800, 1024, 800, 0, 0, 2, 0,
+		{0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 0, 0, 0, 0, 0, 0,
+		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* a2024-15 (Not yet supported) */
+		1024, 800, 1024, 800, 0, 0, 2, 0,
+		{0, 2, 0}, {0, 2, 0}, {0, 2, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 0, 0, 0, 0, 0, 0,
+		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* euro36 */
+		640, 200, 640, 200, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 92, 124, 6, 6, 52, 5,
+		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* euro36-lace */
+		640, 400, 640, 400, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_HIRES, 92, 124, 12, 12, 52, 10,
+		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* euro72 */
+		640, 400, 640, 400, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 164, 92, 9, 9, 80, 8,
+		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* euro72-lace */
+		640, 800, 640, 800, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 164, 92, 18, 18, 80, 16,
+		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* super72 */
+		800, 300, 800, 300, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 212, 140, 10, 11, 80, 7,
+		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* super72-lace */
+		800, 600, 800, 600, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 212, 140, 20, 22, 80, 14,
+		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* dblntsc */
+		640, 200, 640, 200, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 18, 17, 80, 4,
+		0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
+	}, {
+		/* dblntsc-ff */
+		640, 400, 640, 400, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 36, 35, 80, 7,
+		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* dblntsc-lace */
+		640, 800, 640, 800, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 72, 70, 80, 14,
+		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* dblpal */
+		640, 256, 640, 256, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 14, 13, 80, 4,
+		0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
+	}, {
+		/* dblpal-ff */
+		640, 512, 640, 512, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 28, 27, 80, 7,
+		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* dblpal-lace */
+		640, 1024, 640, 1024, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 196, 124, 56, 54, 80, 14,
+		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
+	},
 
-   /*
-    *    Support for AGA
-    */
+	/*
+	 * VGA Video Modes
+	 */
 
-#ifdef CONFIG_AMIFB_AGA
-static int aga_init(void);
-static int aga_encode_fix(struct fb_fix_screeninfo *fix,
-                          struct amiga_fb_par *par);
-static int aga_decode_var(struct fb_var_screeninfo *var,
-                          struct amiga_fb_par *par);
-static int aga_encode_var(struct fb_var_screeninfo *var,
-                          struct amiga_fb_par *par);
-static int aga_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
-                         u_int *transp);
-static int aga_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp);
-static int aga_pan_display(struct fb_var_screeninfo *var,
-                           struct amiga_fb_par *par);
-static void aga_do_vmode(void);
-static void aga_do_blank(int blank);
-static void aga_do_movecursor(void);
-static void aga_do_flashcursor(void);
-
-static int aga_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con);
-static int aga_get_var_cursorinfo(struct fb_var_cursorinfo *var, int con);
-static int aga_set_var_cursorinfo(struct fb_var_cursorinfo *var, int con);
-static int aga_get_cursorstate(struct fb_cursorstate *state, int con);
-static int aga_set_cursorstate(struct fb_cursorstate *state, int con);
-
-static __inline__ void aga_build_clist_hdr(struct clist_hdr *cop);
-static __inline__ void aga_update_clist_hdr(struct clist_hdr *cop,
-                                            struct amiga_fb_par *par);
-static void aga_build_clist_dyn(struct clist_dyn *cop,
-                                struct clist_dyn *othercop, u_short shf,
-                                struct amiga_fb_par *par);
-#endif /* CONFIG_AMIFB_AGA */
+	{
+		/* vga */
+		640, 480, 640, 480, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 64, 96, 30, 9, 112, 2,
+		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	}, {
+		/* vga70 */
+		640, 400, 640, 400, 0, 0, 4, 0,
+		{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+		0, 0, -1, -1, FB_ACCEL_NONE, TAG_SHRES, 64, 96, 35, 12, 112, 2,
+		FB_SYNC_VERT_HIGH_ACT | FB_SYNC_COMP_HIGH_ACT, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
+	},
 
+	/*
+	 * User Defined Video Modes
+	 */
 
-   /*
-    *    Internal routines
-    */
+	{ 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }, { 0, }
+};
 
-static u_long chipalloc(u_long size);
-static void amiga_fb_get_par(struct amiga_fb_par *par);
-static void amiga_fb_set_par(struct amiga_fb_par *par);
-static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive);
-static struct fb_cmap *get_default_colormap(int bpp);
-static int do_fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var,
-                          int kspc);
-static int do_fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var,
-                          int kspc);
-static void do_install_cmap(int con);
-static void memcpy_fs(int fsfromto, void *to, void *from, int len);
-static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto);
-static int alloc_cmap(struct fb_cmap *cmap, int len, int transp);
-static void amiga_fb_set_disp(int con);
-static void amifb_interrupt(int irq, struct pt_regs *fp, void *dummy);
-static char * strtoke(char * s,const char * ct);
-static int get_video_mode(const char *name);
-static void check_default_mode(void);
+#define NUM_USER_MODES   (8)
+#define NUM_TOTAL_MODES  arraysize(amiga_fb_predefined)
+#define NUM_PREDEF_MODES (NUM_TOTAL_MODES-NUM_USER_MODES)
 
+static int amifb_ilbm = 0;	/* interleaved or normal bitplanes */
 
-#ifdef USE_MONO_AMIFB_IF_NON_AGA
+static int amifb_inverse = 0;
+static int amifb_usermode = 0;
 
-/******************************************************************************
-*
-* This is the old monochrome frame buffer device. It's invoked if we're running
-* on a non-AGA machine, until the color support for OCS/ECS is finished.
-*
-******************************************************************************/
-
-/*
- * atari/atafb.c -- Low level implementation of Atari frame buffer device
- * amiga/amifb.c -- Low level implementation of Amiga frame buffer device
- *
- *  Copyright (C) 1994 Martin Schaller & Roman Hodek & Geert Uytterhoeven
- *  
- * 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.
- *
- * History:
- *   - 03 Jan 95: Original version my Martin Schaller: The TT driver and
- *                all the device independent stuff
- *   - 09 Jan 95: Roman: I've added the hardware abstraction (hw_switch)
- *                and wrote the Falcon, ST(E), and External drivers
- *                based on the original TT driver.
- *   - 26 Jan 95: Geert: Amiga version
- *   - 19 Feb 95: Hamish: added Jes Sorensen's ECS patches to the Amiga
- *		  frame buffer device.  This provides ECS support and the
- *		  following screen-modes: multiscan, multiscan-lace,
- * 		  super72, super72-lace, dblntsc, dblpal & euro72.
- *		  He suggests that we remove the old AGA screenmodes,
- *		  as they are non-standard, and some of them doesn't work
- *		  under ECS.
- */
+	/*
+	 * Some default modes
+	 */
 
-static struct mono_mono_amiga_fb_par {
-	u_long smem_start;
-	u_long smem_len;
-	struct geometry *geometry;
-	ushort scr_max_height;			/* screen dimensions */
-	ushort scr_max_width;
-	ushort scr_height;
-	ushort scr_width;
-	ushort scr_depth;
-	int bytes_per_row;			/* offset to one line below */
-	ulong fgcol;
-	ulong bgcol;
-	ulong crsrcol;
-	ushort scroll_latch;			/* Vblank support for hardware scroll */
-	ushort y_wrap;
-	ushort cursor_latch;			/* Hardware cursor */
-	ushort *cursor, *dummy;
-	ushort cursor_flash;
-	ushort cursor_visible;
-	ushort diwstrt_v, diwstrt_h;		/* display window control */
-	ushort diwstop_v, diwstop_h;
-	ushort bplcon0;				/* display mode */
-	ushort htotal;
-	u_char *bitplane[8];			/* pointers to display bitplanes */
-	ulong plane_size;
-	ushort *coplist1hdr;			/* List 1 static  component */
-	ushort *coplist1dyn;			/* List 1 dynamic component */
-	ushort *coplist2hdr;			/* List 2 static  component */
-	ushort *coplist2dyn;			/* List 2 dynamic component */
-} mono_current_par;
-
-
-static ushort mono_cursor_data[] =
-{
-    0x2c81,0x2d00,
-    0xf000,0x0000,
-    0x0000,0x0000
-};
+#define DEFMODE_PAL        "pal"	/* for PAL OCS/ECS */
+#define DEFMODE_NTSC       "ntsc"	/* for NTSC OCS/ECS */
+#define DEFMODE_AMBER_PAL  "pal-lace"	/* for flicker fixed PAL (A3000) */
+#define DEFMODE_AMBER_NTSC "ntsc-lace"	/* for flicker fixed NTSC (A3000) */
+#define DEFMODE_AGA        "vga70"	/* for AGA */
 
+	/*
+	 * Macros for the conversion from real world values to hardware register
+	 * values
+	 *
+	 * This helps us to keep our attention on the real stuff...
+	 *
+	 * Hardware limits for AGA:
+	 *
+	 *	parameter  min    max  step
+	 *	---------  ---   ----  ----
+	 *	diwstrt_h    0   2047     1
+	 *	diwstrt_v    0   2047     1
+	 *	diwstop_h    0   4095     1
+	 *	diwstop_v    0   4095     1
+	 *
+	 *	ddfstrt      0   2032    16
+	 *	ddfstop      0   2032    16
+	 *
+	 *	htotal       8   2048     8
+	 *	hsstrt       0   2040     8
+	 *	hsstop       0   2040     8
+	 *	vtotal       1   4096     1
+	 *	vsstrt       0   4095     1
+	 *	vsstop       0   4095     1
+	 *	hcenter      0   2040     8
+	 *
+	 *	hbstrt       0   2047     1
+	 *	hbstop       0   2047     1
+	 *	vbstrt       0   4095     1
+	 *	vbstop       0   4095     1
+	 *
+	 * Horizontal values are in 35 ns (SHRES) pixels
+	 * Vertical values are in half scanlines
+	 */
 
-/*
- *  Color definitions
- */
+/* bplcon1 (smooth scrolling) */
 
-#define FG_COLOR		(0x000000)	/* black */
-#define BG_COLOR		(0xaaaaaa)	/* lt. grey */
-#define CRSR_COLOR		(0xff0000)	/* bright red */
-
-#define FG_COLOR_INV		BG_COLOR
-#define BG_COLOR_INV		FG_COLOR
-#define CRSR_COLOR_INV		(0x6677aa)	/* a blue-ish color */
+#define hscroll2hw(hscroll) \
+	(((hscroll)<<12 & 0x3000) | ((hscroll)<<8 & 0xc300) | \
+	 ((hscroll)<<4 & 0x0c00) | ((hscroll)<<2 & 0x00f0) | ((hscroll)>>2 & 0x000f))
 
-/*
- *  Split 24-bit RGB colors in 12-bit MSB (for OCS/ECS/AGA) and LSB (for AGA)
- */
+/* diwstrt/diwstop/diwhigh (visible display window) */
 
-#define COLOR_MSB(rgb)	(((rgb>>12)&0xf00)|((rgb>>8)&0x0f0)|((rgb>>4)&0x00f))
-#define COLOR_LSB(rgb)	(((rgb>>8) &0xf00)|((rgb>>4)&0x0f0)|((rgb)   &0x00f))
+#define diwstrt2hw(diwstrt_h, diwstrt_v) \
+	(((diwstrt_v)<<7 & 0xff00) | ((diwstrt_h)>>2 & 0x00ff))
+#define diwstop2hw(diwstop_h, diwstop_v) \
+	(((diwstop_v)<<7 & 0xff00) | ((diwstop_h)>>2 & 0x00ff))
+#define diwhigh2hw(diwstrt_h, diwstrt_v, diwstop_h, diwstop_v) \
+	(((diwstop_h)<<3 & 0x2000) | ((diwstop_h)<<11 & 0x1800) | \
+	 ((diwstop_v)>>1 & 0x0700) | ((diwstrt_h)>>5 & 0x0020) | \
+	 ((diwstrt_h)<<3 & 0x0018) | ((diwstrt_v)>>9 & 0x0007))
 
-/* Cursor definitions */
+/* ddfstrt/ddfstop (display DMA) */
 
-#define CRSR_FLASH		1	/* Cursor flashing on(1)/off(0) */
-#define CRSR_BLOCK		1	/* Block(1) or line(0) cursor */
+#define ddfstrt2hw(ddfstrt)	div8(ddfstrt)
+#define ddfstop2hw(ddfstop)	div8(ddfstop)
 
+/* hsstrt/hsstop/htotal/vsstrt/vsstop/vtotal/hcenter (sync timings) */
 
-/* controlling screen blanking (read in VBL handler) */
-static int mono_do_blank;
-static int mono_do_unblank;
-static unsigned short mono_save_bplcon3;
+#define hsstrt2hw(hsstrt)	(div8(hsstrt))
+#define hsstop2hw(hsstop)	(div8(hsstop))
+#define htotal2hw(htotal)	(div8(htotal)-1)
+#define vsstrt2hw(vsstrt)	(div2(vsstrt))
+#define vsstop2hw(vsstop)	(div2(vsstop))
+#define vtotal2hw(vtotal)	(div2(vtotal)-1)
+#define hcenter2hw(htotal)	(div8(htotal))
 
-/*
- * mono_ecs_color_zero is used to keep custom.color[0] for the special ECS color-
- * table, as custom.color[0] is cleared at vblank interrupts.
- * -Jes (jds@kom.auc.dk)
- */
+/* hbstrt/hbstop/vbstrt/vbstop (blanking timings) */
 
-static ushort mono_ecs_color_zero;
+#define hbstrt2hw(hbstrt)	(((hbstrt)<<8 & 0x0700) | ((hbstrt)>>3 & 0x00ff))
+#define hbstop2hw(hbstop)	(((hbstop)<<8 & 0x0700) | ((hbstop)>>3 & 0x00ff))
+#define vbstrt2hw(vbstrt)	(div2(vbstrt))
+#define vbstop2hw(vbstop)	(div2(vbstop))
+
+/* colour */
+
+#define rgb2hw8_high(red, green, blue) \
+	(((red)<<4 & 0xf00) | ((green) & 0x0f0) | ((blue)>>4 & 0x00f))
+#define rgb2hw8_low(red, green, blue) \
+	(((red)<<8 & 0xf00) | ((green)<<4 & 0x0f0) | ((blue) & 0x00f))
+#define rgb2hw4(red, green, blue) \
+	(((red)<<8 & 0xf00) | ((green)<<4 & 0x0f0) | ((blue) & 0x00f))
+#define rgb2hw2(red, green, blue) \
+	(((red)<<10 & 0xc00) | ((green)<<6 & 0x0c0) | ((blue)<<2 & 0x00c))
 
-static struct {
-	int right_count;
-	int done;
-} mono_vblank;
+/* sprpos/sprctl (sprite positioning) */
 
+#define spr2hw_pos(start_v, start_h) \
+	(((start_v)<<7&0xff00) | ((start_h)>>3&0x00ff))
+#define spr2hw_ctl(start_v, start_h, stop_v) \
+	(((stop_v)<<7&0xff00) | ((start_v)>>4&0x0040) | ((stop_v)>>5&0x0020) | \
+	 ((start_h)<<3&0x0018) | ((start_v)>>7&0x0004) | ((stop_v)>>8&0x0002) | \
+	 ((start_h)>>2&0x0001))
 
-static __inline__ void mono_init_vblank(void)
-{
-	mono_vblank.right_count = 0;
-	mono_vblank.done = 0;
-}
+/* get current vertical position of beam */
+#define get_vbpos()	((u_short)((*(u_long volatile *)&custom.vposr >> 7) & 0xffe))
 
-/* Geometry structure contains all useful information about given mode.
- *
- * Strictly speaking `scr_max_height' and `scr_max_width' is redundant
- * information with DIWSTRT value provided. Might be useful if modes
- * can be hotwired by user in future. It fits for the moment.
- *
- * At the moment, the code only distinguishes between OCS and AGA. ECS
- * lies somewhere in between - someone more familiar with it could make
- * appropriate modifications so that some advanced display modes are
- * available, without confusing the poor chipset. OCS modes use only the
- * bplcon0, diwstrt, diwstop, ddfstrt, ddfstop registers (a few others could
- * be used as well). -wjr
- *
- * The code now supports ECS as well, except for FMODE all control registers
- * are the same under ECS. A special color-table has to be generated though.
- * -Jes
- */
-struct geometry {
-    char *modename;	/* Name this thing */
-    char isOCS;		/* Is it OCS or ECS/AGA */
-    ushort bplcon0;	/* Values for bit plane control register 0 */
-    ushort scr_width;
-    ushort scr_height;
-    ushort scr_depth;
-    ushort scr_max_width;     /* Historical, might be useful still */
-    ushort scr_max_height;
-    ushort diwstrt_h;	/* Where the display window starts */
-    ushort diwstrt_v;
-    ushort alignment;	/* Pixels per scanline must be a multiple of this */
-    /* OCS doesn't need anything past here */
-    ushort bplcon2;
-    ushort bplcon3;
-    /* The rest of these control variable sync */
-    ushort htotal;	/* Total hclocks */
-    ushort hsstrt;	/* HSYNC start and stop */
-    ushort hsstop;
-    ushort hbstrt;	/* HBLANK start and stop */
-    ushort hbstop;
-    ushort vtotal;	/* Total vlines */
-    ushort vsstrt;	/* VSYNC, VBLANK ditto */
-    ushort vsstop;
-    ushort vbstrt;
-    ushort vbstop;
-    ushort hcenter;	/* Center of line, for interlaced modes */
-    ushort beamcon0;	/* Beam control */
-    ushort fmode;	/* Memory fetch mode */
-};
+	/*
+	 * Copper Initialisation List
+	 */
 
-#define MAX_COP_LIST_ENTS 64
-#define COP_MEM_REQ       (MAX_COP_LIST_ENTS*4*2)
-#define SPR_MEM_REQ       (24)
+#define COPINITSIZE (sizeof(copins)*40)
 
+enum {
+	cip_bplcon0
+};
 
-static struct geometry mono_modes[] = {
-	/* NTSC modes: !!! can't guarantee anything about overscan modes !!! */
-	{
-		"ntsc-lace", 1,
-		BPC0_HIRES | BPC0_LACE,
-		640, 400, 1,
-		704, 480,
-		0x71, 0x18,		/* diwstrt h,v */
-		16			/* WORD aligned */
-	}, {
-		"ntsc", 1,
-		BPC0_HIRES,
-		640, 200, 1,
-		704, 240,
-		0x71, 0x18,
-		16			/* WORD aligned */
-	}, {
-		"ntsc-lace-over", 1,
-		BPC0_HIRES | BPC0_LACE,
-		704, 480, 1,
-		704, 480,
-		0x71, 0x18,
-		16			/* WORD aligned */
-	}, {
-		"ntsc-over", 1,
-		BPC0_HIRES,
-		704, 240, 1,
-		704, 240,
-		0x71, 0x18,
-		16			/* WORD aligned */
-	},
-	/* PAL modes. Warning:
-	 * 
-	 * PAL overscan causes problems on my machine because maximum diwstop_h
-	 * value seems to be ~0x1c2, rather than 0x1e0+ inferred by RKM 1.1
-	 * and 0x1d5 inferred by original `amicon.c' source. Is this a hardware
-	 * limitation of OCS/pal or 1084?. Or am I doing something stupid here?
-	 *
-	 * Included a couple of overscan modes that DO work on my machine,
-	 * although not particularly useful.
+	/*
+	 * Long Frame/Short Frame Copper List
+	 * Don't change the order, build_copper()/rebuild_copper() rely on this
 	 */
-	{
-		"pal-lace", 1,
-		BPC0_HIRES | BPC0_LACE,
-		640, 512, 1,
-		704, 592,
-		0x71, 0x18,
-		16			/* WORD aligned */
-	}, {
-		"pal", 1,
-		BPC0_HIRES,
-		640, 256, 1,
-		704, 296,
-		0x71, 0x18,
-		16			/* WORD aligned */
-	}, {
-		"pal-lace-over", 1,
-		BPC0_HIRES | BPC0_LACE,
-		704, 592, 1,
-		704, 582,
-		0x5b, 0x18,
-		16			/* WORD aligned */
-	}, {
-		"pal-over", 1,
-		BPC0_HIRES,
-		704, 296, 1,
-		704, 296,
-		0x5b, 0x18,
-		16			/* WORD aligned */
-
-	},
-	/* ECS modes, these are real ECS modes */
-	{
-		"multiscan", 0,
-		BPC0_SHRES | BPC0_ECSENA,	/* bplcon0 */
-		640, 480, 1,
-		640, 480,
-		0x0041, 0x002c,			/* diwstrt h,v */
-		64,	        		/* 64-bit aligned */
-		BPC2_KILLEHB,			/* bplcon2 */
-		BPC3_PF2OF1 | BPC3_PF2OF0 | BPC3_SPRES1 | BPC3_SPRES0 |
-			BPC3_BRDRBLNK | BPC3_EXTBLKEN,	/* bplcon3 */
-
-		0x0072,				/* htotal */
-		0x000a,				/* hsstrt */
-		0x0013,				/* hsstop */
-		0x0002,				/* hbstrt */
-		0x001c,				/* hbstop */
-		0x020c,				/* vtotal */
-		0x0008,				/* vsstrt */
-		0x0011,				/* vsstop */
-		0x0000,				/* vbstrt */
-		0x001c,				/* vbstop */
-		0x0043,				/* hcenter */
-		BMC0_VARVBEN | BMC0_LOLDIS | BMC0_VARVSYEN |
-			BMC0_VARHSYEN | BMC0_VARBEAMEN | BMC0_BLANKEN,
-						/* beamcon0 */
-		FMODE_BPAGEM | FMODE_BPL32	/* fmode */
-	},
-	{
-		"multiscan-lace", 0,
-		BPC0_SHRES | BPC0_LACE | BPC0_ECSENA,	/* bplcon0 */
-		640, 960, 1,
-		640, 960,
-		0x0041, 0x002c,			/* diwstrt h,v */
-		64,	        		/* 64-bit aligned */
-		BPC2_KILLEHB,			/* bplcon2 */
-		BPC3_PF2OF1 | BPC3_PF2OF0 | BPC3_SPRES1 | BPC3_SPRES0 |
-			BPC3_BRDRBLNK | BPC3_EXTBLKEN,	/* bplcon3 */
-
-		0x0072,				/* htotal */
-		0x000a,				/* hsstrt */
-		0x0013,				/* hsstop */
-		0x0002,				/* hbstrt */
-		0x001c,				/* hbstop */
-		0x020c,				/* vtotal */
-		0x0008,				/* vsstrt */
-		0x0011,				/* vsstop */
-		0x0000,				/* vbstrt */
-		0x001c,				/* vbstop */
-		0x0043,				/* hcenter */
-		BMC0_VARVBEN | BMC0_LOLDIS | BMC0_VARVSYEN |
-			BMC0_VARHSYEN | BMC0_VARBEAMEN | BMC0_BLANKEN,
-						/* beamcon0 */
-		FMODE_BPAGEM | FMODE_BPL32	/* fmode */
-	},
-	/* Super 72 - 800x300 72Hz noninterlaced mode. */
-	{
-		"super72", 0,
-		BPC0_SHRES | BPC0_ECSENA,	/* bplcon0 */
-		800, 304, 1,			/* need rows%8 == 0 */
-		800, 304,			/* (cols too) */
-		0x0051, 0x0021,			/* diwstrt h,v */
-		64,				/* 64-bit aligned */
-		BPC2_KILLEHB,			/* bplcon2 */
-		BPC3_PF2OF1 | BPC3_PF2OF0 | BPC3_SPRES1 | BPC3_SPRES0 |
-			BPC3_BRDRBLNK | BPC3_EXTBLKEN,	/* bplcon3 */
-		0x0091,				/* htotal */
-		0x000a,				/* hsstrt */
-		0x0013,				/* hsstop */
-		0x0001,				/* hbstrt */
-		0x001e,				/* hbstop */
-		0x0156,				/* vtotal */
-		0x0009,				/* vsstrt */
-		0x0012,				/* vsstop */
-		0x0000,				/* vbstrt */
-		0x001c,				/* vbstop */
-		0x0052,				/* hcenter */
-		BMC0_VARVBEN | BMC0_LOLDIS | BMC0_VARVSYEN |
-			BMC0_VARHSYEN | BMC0_VARBEAMEN | BMC0_BLANKEN,
-						/* beamcon0 */
-		FMODE_BPAGEM | FMODE_BPL32	/* fmode */
-	},
-	/* Super 72 lace - 800x600 72Hz interlaced mode. */
-	{
-		"super72-lace", 0,
-		BPC0_SHRES | BPC0_LACE | BPC0_ECSENA,	/* bplcon0 */
-		800, 600, 1,			/* need rows%8 == 0 */
-		800, 600,			/* (cols too) */
-		0x0051, 0x0021,			/* diwstrt h,v */
-		64,     			/* 64-bit aligned */
-		BPC2_KILLEHB,			/* bplcon2 */
-		BPC3_PF2OF1 | BPC3_PF2OF0 | BPC3_SPRES1 | BPC3_SPRES0 |
-			BPC3_BRDRBLNK | BPC3_EXTBLKEN,
-						/* bplcon3 */
-		0x0091,				/* htotal */
-		0x000a,				/* hsstrt */
-		0x0013,				/* hsstop */
-		0x0001,				/* hbstrt */
-		0x001e,				/* hbstop */
-		0x0150,				/* vtotal */
-		0x0009,				/* vsstrt */
-		0x0012,				/* vsstop */
-		0x0000,				/* vbstrt */
-		0x001c,				/* vbstop */
-		0x0052,				/* hcenter */
-		BMC0_VARVBEN | BMC0_LOLDIS | BMC0_VARVSYEN |
-			BMC0_VARHSYEN | BMC0_VARBEAMEN | BMC0_BLANKEN,
-						/* beamcon0 */
-		FMODE_BPAGEM | FMODE_BPL32	/* fmode */
-	},
-	/* DblNtsc - 640x400 59Hz noninterlaced mode. */
-	{
-		"dblntsc", 0,
-		BPC0_SHRES | BPC0_ECSENA,	/* bplcon0 */
-		640, 400, 1,			/* need rows%8 == 0 */
-		640, 400,			/* (cols too) */
-		0x0049, 0x0021,			/* diwstrt h,v */
-		64,				/* 64-bit aligned */
-		BPC2_KILLEHB,			/* bplcon2 */
-		BPC3_PF2OF1 | BPC3_PF2OF0 | BPC3_SPRES1 | BPC3_SPRES0 |
-			BPC3_BRDRBLNK | BPC3_EXTBLKEN,
-						/* bplcon3 */
-		0x0079,				/* htotal */
-		0x0007,				/* hsstrt */
-		0x0013,				/* hsstop */
-		0x0001,				/* hbstrt */
-		0x001e,				/* hbstop */
-		0x01ec,				/* vtotal */
-		0x0008,				/* vsstrt */
-		0x0010,				/* vsstop */
-		0x0000,				/* vbstrt */
-		0x0019,				/* vbstop */
-		0x0046,				/* hcenter */
-		BMC0_VARVBEN | BMC0_LOLDIS | BMC0_VARVSYEN |
-			BMC0_VARHSYEN | BMC0_VARBEAMEN | BMC0_BLANKEN,
-						/* beamcon0 */
-		FMODE_BPAGEM | FMODE_BPL32	/* fmode */
-	},
-	/* DblPal - 640x512 52Hz noninterlaced mode. */
-	{
-		"dblpal", 0,
-		BPC0_SHRES | BPC0_ECSENA,	/* bplcon0 */
-		640, 512, 1,			/* need rows%8 == 0 */
-		640, 512,			/* (cols too) */
-		0x0049, 0x0021,			/* diwstrt h,v */
-		64,				/* 64-bit aligned */
-		BPC2_KILLEHB,			/* bplcon2 */
-		BPC3_PF2OF1 | BPC3_PF2OF0 | BPC3_SPRES1 | BPC3_SPRES0 |
-			BPC3_BRDRBLNK | BPC3_EXTBLKEN,
-						/* bplcon3 */
-		0x0079,				/* htotal */
-		0x0007,				/* hsstrt */
-		0x0013,				/* hsstop */
-		0x0001,				/* hbstrt */
-		0x001e,				/* hbstop */
-		0x0234,				/* vtotal */
-		0x0008,				/* vsstrt */
-		0x0010,				/* vsstop */
-		0x0000,				/* vbstrt */
-		0x0019,				/* vbstop */
-		0x0046,				/* hcenter */
-		BMC0_VARVBEN | BMC0_LOLDIS | BMC0_VARVSYEN |
-			BMC0_VARHSYEN | BMC0_VARBEAMEN | BMC0_BLANKEN,
-						/* beamcon0 */
-		FMODE_BPAGEM | FMODE_BPL32	/* fmode */
-	},
-	/* Euro72 - productivity - 640x400 71Hz noninterlaced mode. */
-	{
-		"euro72", 0,
-		BPC0_SHRES | BPC0_ECSENA,	/* bplcon0 */
-		640, 400, 1,			/* need rows%8 == 0 */
-		640, 400,			/* (cols too) */
-		0x0041, 0x0021,			/* diwstrt h,v */
-		64,				/* 64-bit aligned */
-		BPC2_KILLEHB,			/* bplcon2 */
-		BPC3_PF2OF1 | BPC3_PF2OF0 | BPC3_SPRES1 | BPC3_SPRES0 |
-			BPC3_BRDRBLNK | BPC3_EXTBLKEN,
-						/* bplcon3 */
-		0x0071,				/* htotal */
-		0x0009,				/* hsstrt */
-		0x0013,				/* hsstop */
-		0x0001,				/* hbstrt */
-		0x001e,				/* hbstop */
-		0x01be,				/* vtotal */
-		0x0008,				/* vsstrt */
-		0x0016,				/* vsstop */
-		0x0000,				/* vbstrt */
-		0x001f,				/* vbstop */
-		0x0041,				/* hcenter */
-		BMC0_VARVBEN | BMC0_LOLDIS | BMC0_VARVSYEN |
-			BMC0_VARHSYEN | BMC0_VARBEAMEN | BMC0_BLANKEN,
-						/* beamcon0 */
-		FMODE_BPAGEM | FMODE_BPL32	/* fmode */
-	},
-	/* AGA modes */
-	{
-	/*
-	 * A 640x480, 60Hz noninterlaced AGA mode. It would be nice to be
-	 * able to have some of these values computed dynamically, but that
-	 * requires more knowledge of AGA than I have. At the moment,
-	 * the values make it centered on my 1960 monitor. -wjr
-	 *
-	 * For random reasons to do with the way arguments are parsed,
-	 * these names can't start with a digit.
-	 *
-	 * Don't count on being able to reduce scr_width and scr_height
-	 * and ending up with a smaller but well-formed screen - this
-	 * doesn't seem to work well at the moment.
-	 */
-		"aga640x480", 0,
-		BPC0_SHRES | BPC0_ECSENA,			/* bplcon0 */
-		640, 480, 1,
-		640, 480,
-		0x0041, 0x002b,						/* diwstrt h,v */
-		64,										/* 64-bit aligned */
-		BPC2_KILLEHB,							/* bplcon2 */
-		BPC3_PF2OF1 | BPC3_PF2OF0 | BPC3_SPRES1 | BPC3_SPRES0 |
-		BPC3_BRDRBLNK | BPC3_EXTBLKEN,	/* bplcon3 */
-		0x0071,									/* htotal */
-		0x000c,									/* hsstrt */
-		0x001c,									/* hsstop */
-		0x0008,									/* hbstrt */
-		0x001e,									/* hbstop */
-		0x020c,									/* vtotal */
-		0x0001,									/* vsstrt */
-		0x0003,									/* vsstop */
-		0x0000,									/* vbstrt */
-		0x000f,									/* vbstop */
-		0x0046,									/* hcenter */
-		BMC0_HARDDIS | BMC0_VARVBEN | BMC0_LOLDIS | BMC0_VARVSYEN |
-			BMC0_VARHSYEN | BMC0_VARBEAMEN | BMC0_BLANKEN,	/* beamcon0 */
-		FMODE_BPAGEM | FMODE_BPL32			/* fmode */
-	}, {
-		/* An 800x600 72Hz interlaced mode. */
-		"aga800x600", 0,
-		BPC0_SHRES | BPC0_LACE | BPC0_ECSENA,	/* bplcon0 */
-		896, 624, 1,							/* need rows%8 == 0 */
-		896, 624,								/* (cols too) */
-		0x0041, 0x001e,						/* diwstrt h,v */
-		64,						/* 64-bit aligned */
-		BPC2_KILLEHB,							/* bplcon2 */
-		BPC3_PF2OF1 | BPC3_PF2OF0 | BPC3_SPRES1 | BPC3_SPRES0 |
-		BPC3_BRDRBLNK | BPC3_EXTBLKEN,	/* bplcon3 */
-		0x0091,									/* htotal */
-		0x000e,									/* hsstrt */
-		0x001d,									/* hsstop */
-		0x000a,									/* hbstrt */
-		0x001e,									/* hbstop */
-		0x0156,									/* vtotal */
-		0x0001,									/* vsstrt */
-		0x0003,									/* vsstop */
-		0x0000,									/* vbstrt */
-		0x000f,									/* vbstop */
-		0x0050,									/* hcenter */
-		BMC0_HARDDIS | BMC0_VARVBEN | BMC0_LOLDIS | BMC0_VARVSYEN |
-		BMC0_VARHSYEN | BMC0_VARBEAMEN | BMC0_BLANKEN,	/* beamcon0 */
-		FMODE_BPAGEM | FMODE_BPL32			/* fmode */
-	},
-	/*
-	 * Additional AGA modes by Geert Uytterhoeven
-	 */
-	{
-		/*
-		 * A 720x400, 70 Hz noninterlaced AGA mode (29.27 kHz)
-		 */
-		"aga720x400", 0,
-		BPC0_SHRES | BPC0_ECSENA,			/* bplcon0 */
-		720, 400, 1,
-		720, 400,
-		0x0041, 0x0013,						/* diwstrt h,v */
-		64,										/* 64-bit aligned */
-		BPC2_KILLEHB,							/* bplcon2 */
-		BPC3_PF2OF1 | BPC3_PF2OF0 | BPC3_SPRES1 | BPC3_SPRES0 |
-		BPC3_BRDRBLNK | BPC3_EXTBLKEN,	/* bplcon3 */
-		0x0079,									/* htotal */
-		0x000e,									/* hsstrt */
-		0x0018,									/* hsstop */
-		0x0001,									/* hbstrt */
-		0x0021,									/* hbstop */
-		0x01a2,									/* vtotal */
-		0x0003,									/* vsstrt */
-		0x0005,									/* vsstop */
-		0x0000,									/* vbstrt */
-		0x0012,									/* vbstop */
-		0x0046,									/* hcenter */
-		BMC0_VARVBEN | BMC0_LOLDIS | BMC0_VARVSYEN | BMC0_VARHSYEN |
-		BMC0_VARBEAMEN | BMC0_PAL | BMC0_VARCSYEN | BMC0_CSYTRUE |
-		BMC0_VSYTRUE,							/* beamcon0 */
-		FMODE_BPAGEM | FMODE_BPL32			/* fmode */
-	}, {
-		/*
-		 * A 640x400, 76 Hz noninterlaced AGA mode (31.89 kHz)
-		 */
-		"aga640x400", 0,
-		BPC0_SHRES | BPC0_ECSENA,			/* bplcon0 */
-		640, 400, 1,
-		640, 400,
-		0x0041, 0x0015,						/* diwstrt h,v */
-		64,										/* 64-bit aligned */
-		BPC2_KILLEHB,							/* bplcon2 */
-		BPC3_PF2OF1 | BPC3_PF2OF0 | BPC3_SPRES1 | BPC3_SPRES0 |
-		BPC3_BRDRBLNK | BPC3_EXTBLKEN,	/* bplcon3 */
-		0x006f,									/* htotal */
-		0x000d,									/* hsstrt */
-		0x0018,									/* hsstop */
-		0x0001,									/* hbstrt */
-		0x0021,									/* hbstop */
-		0x01a4,									/* vtotal */
-		0x0003,									/* vsstrt */
-		0x0005,									/* vsstop */
-		0x0000,									/* vbstrt */
-		0x0014,									/* vbstop */
-		0x0046,									/* hcenter */
-		BMC0_VARVBEN | BMC0_LOLDIS | BMC0_VARVSYEN | BMC0_VARHSYEN |
-		BMC0_VARBEAMEN | BMC0_PAL | BMC0_VARCSYEN | BMC0_CSYTRUE |
-		BMC0_VSYTRUE,							/* beamcon0 */
-		FMODE_BPAGEM | FMODE_BPL32			/* fmode */
-	}, {
-		/*
-		 * A 640x480, 64 Hz noninterlaced AGA mode (31.89 kHz)
-		 */
-		"aga640x480a", 0,
-		BPC0_SHRES | BPC0_ECSENA,			/* bplcon0 */
-		640, 480, 1,
-		640, 480,
-		0x0041, 0x0015,						/* diwstrt h,v */
-		64,										/* 64-bit aligned */
-		BPC2_KILLEHB,							/* bplcon2 */
-		BPC3_PF2OF1 | BPC3_PF2OF0 | BPC3_SPRES1 | BPC3_SPRES0 |
-		BPC3_BRDRBLNK | BPC3_EXTBLKEN,	/* bplcon3 */
-		0x006f,									/* htotal */
-		0x000e,									/* hsstrt */
-		0x0018,									/* hsstop */
-		0x0001,									/* hbstrt */
-		0x0021,									/* hbstop */
-		0x01f4,									/* vtotal */
-		0x0003,									/* vsstrt */
-		0x0005,									/* vsstop */
-		0x0000,									/* vbstrt */
-		0x0014,									/* vbstop */
-		0x0046,									/* hcenter */
-		BMC0_VARVBEN | BMC0_LOLDIS | BMC0_VARVSYEN | BMC0_VARHSYEN |
-		BMC0_VARBEAMEN | BMC0_PAL | BMC0_VARCSYEN | BMC0_CSYTRUE |
-		BMC0_VSYTRUE,							/* beamcon0 */
-		FMODE_BPAGEM | FMODE_BPL32			/* fmode */
-	}
-};
 
-#define	NMODES	(sizeof(mono_modes) / sizeof(struct geometry))
+#define COPLISTSIZE (sizeof(copins)*64)
 
-static struct fb_var_screeninfo mono_mono_amiga_fb_predefined[] = {
-	{ /* autodetect */
-		0, 0, 0, 0, 0, 0, 0, 0,   		/* xres-grayscale */
-		{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, 	/* red green blue tran*/
-		0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0 }
+enum {
+	cop_wait, cop_bplcon0,
+	cop_spr0ptrh, cop_spr0ptrl,
+	cop_diwstrt, cop_diwstop,
+	cop_diwhigh,
 };
 
-static int mono_num_mono_amiga_fb_predefined= sizeof(mono_mono_amiga_fb_predefined)/sizeof(struct fb_var_screeninfo);
-
-
-
-/* Some default modes */
-#define OCS_PAL_LOWEND_DEFMODE	5	/* PAL non-laced for 500/2000 */
-#define OCS_PAL_3000_DEFMODE		4	/* PAL laced for 3000 */
-#define OCS_NTSC_LOWEND_DEFMODE	1	/* NTSC non-laced for 500/2000 */
-#define OCS_NTSC_3000_DEFMODE		0	/* NTSC laced for 3000 */
-#define AGA_DEFMODE					8	/* 640x480 non-laced for AGA */
-
-static int mono_amifb_inverse = 0;
-static int mono_amifb_mode = -1;
-
-static void mono_video_setup (char *options, int *ints)
-{
-	char *this_opt;
-	int i;
-
-	fb_info.fontname[0] = '\0';
-
-	if (!options || !*options)
-		return;
-
-	for (this_opt = strtok(options,","); this_opt; this_opt = strtok(NULL,","))
-		if (!strcmp (this_opt, "inverse"))
-			mono_amifb_inverse = 1;
-      else if (!strncmp(this_opt, "font:", 5))
-               strcpy(fb_info.fontname, this_opt+5);
-		else
-			for (i = 0; i < NMODES; i++)
-				if (!strcmp(this_opt, mono_modes[i].modename)) {
-					mono_amifb_mode = i;
-					break;
-				}
-}
-
-/* Notes about copper scrolling:
- *
- * 1. The VBLANK routine dynamically rewrites a LIVE copper list that is
- *    currently being executed. Don't mess with it unless you know the
- *    complications. Fairly sure that double buffered lists doesn't
- *    make our life any easier.
- *
- * 2. The vblank code starts executing at logical line 0. Display must be
- *    set up and ready to run by line DIWSTRT_V, typically 0x2c, minimum
- *    value is 0x18 for maximum overscan.
- *
- *    Tests on my A500/030 for dynamically generating a 37 element copper
- *    list during the VBLANK period under AmigaDos required between
- *    0x10 and 0x14 scanlines. This should be pathological case, and
- *    should do better under Linux/68k. It is however IMPERATIVE that I am
- *    first in the VBLANK isr chain. Try to keep 'buildclist' as fast as
- *    possible. Don't think that it justifies assembler thou'
- *
- * 3. PAL 640x256 display (no overscan) has copper-wait y positions in range
- *    0x02c -> 0x12c. NTSC overscan uses values > 256 too. However counter
- *    is 8 bit, will wrap. RKM 1.1 suggests use of a WAIT(0x00,0xff),
- *    WAIT(x,y-0x100) pair to handle this case. This is WRONG - must use
- *    WAIT(0xe2,0xff) to ensure that wrap occurred by next copper
- *    instruction. Argghh!
- *
- * 4. RKM 1.1 suggests Copper-wait x positions are in range [0,0xe2].
- *    Horizontal blanking occurs in range 0x0f -> 0x35. Black screen
- *    shown in range 0x04 -> 0x47.
- *
- *    Experiments suggest that using WAIT(0x00,y), we can replace up to
- *    7 bitplane pointers before display fetch start. Using a 
- *    WAIT(0xe0,y-1) instead, we can replace 8 pointers that should be
- *    all that we need for a full AGA display. Should work because of
- *    fetch latency with bitmapped display.
- *
- *    I think that this works. Someone please tell me if something breaks.
- *
- * Is diwstop_h the right value to use for "close to the end of line"?
- * It seems to work for me, at least for the modes I've defined. -wjr
- *
- * I changed the Wait(diwstop_h, 0xff) for 256-line chunk skipping to
- * Wait(diwstop_h-2, 0xff) to make it work with the additional
- * `get-all-you-can-get-out-of-it' AGA modes. Maybe we should derive the
- * wait position from the HTOTAL value? - G.U.
- *
- * The Wait(diwstop_h-2, 0xff) didn't work in Super72 under ECS, instead
- * I changed it to Wait(htotal-4, 0xff). Dunno whether it works under AGA,
- * and don't ask my why it works. I'm trying to get some facts on this issue
- * from Commodore.
- * -Jes
- */
-
-static __inline__ ushort *mono_build_clist_hdr(register struct display *p,
-														ushort *cop,
-														ushort *othercop)	/* Interlace: List for next frame */
-{
-	int i;
-	ushort diwstrt_v = mono_current_par.diwstrt_v;
-	ushort diwstop_h = mono_current_par.diwstop_h;
-
-	if (othercop) {
-		*cop++ = CUSTOM_OFS(cop1lc);
-		*cop++ = (long)othercop >> 16;
-		*cop++ = CUSTOM_OFS(cop1lc) + 2;
-		*cop++ = (long)othercop;
-	}
-
-	/* Point Sprite 0 at cursor sprite: */
-	*cop++ = CUSTOM_OFS(sprpt[0]);
-	*cop++ = (ushort)((long)mono_current_par.cursor >> 16);
-	*cop++ = CUSTOM_OFS(sprpt[0]) + 2;
-	*cop++ = (ushort)((long)mono_current_par.cursor & 0x0000ffff);
-
-	/* Point Sprites 1-7 at dummy sprite: */
-	for (i=1; i<8; i++) {
-		*cop++ = CUSTOM_OFS(sprpt[i]);
-		*cop++ = (ushort)((long)mono_current_par.dummy >> 16);
-		*cop++ = CUSTOM_OFS(sprpt[i]) + 2;
-		*cop++ = (ushort)((long)mono_current_par.dummy & 0x0000ffff);
-	}
-
-	/* Halt copper until we have rebuilt the display list */
-
-	*cop++ = ((diwstrt_v - 2) << 8) | (diwstop_h >> 1) | 0x1;
-	*cop++ = 0xfffe;
-
-	return(cop);
-}
-
-static __inline__ ushort *mono_build_clist_dyn(register struct display *p,
-														ushort *cop,
-														int shf)				/* Interlace: Short frame */
-{
-	ushort diwstrt_v = mono_current_par.diwstrt_v;
-	ushort diwstop_h = mono_current_par.diwstop_h;
-	ushort y_wrap = mono_current_par.y_wrap;
-	ulong offset = y_wrap * mono_current_par.bytes_per_row;
-	long scrmem;
-	int i;
-
-	/* Set up initial bitplane ptrs */
-
-	for (i = 0 ; i < mono_current_par.scr_depth ; i++) {
-		scrmem    = ((long)mono_current_par.bitplane[i]) + offset;
-
-		if (shf)
-			scrmem += mono_current_par.bytes_per_row;
-
-		*cop++ = CUSTOM_OFS(bplpt[i]);
-		*cop++ = (long)scrmem >> 16;
-		*cop++ = CUSTOM_OFS(bplpt[i]) + 2;
-		*cop++ = (long)scrmem;
-	}
-
-	/* If wrapped frame needed - wait for line then switch bitplXs */
-
-	if (y_wrap) {
-		ushort line;
-        
-		if (mono_current_par.bplcon0 & BPC0_LACE)
-			line = diwstrt_v + (mono_current_par.scr_height - y_wrap)/2;
-		else
-			line = diwstrt_v + mono_current_par.scr_height - y_wrap;
-
-		/* Handle skipping over 256-line chunks */
-		while (line > 256) {
-			/* Hardware limitation - 8 bit counter    */
-			/* Wait(diwstop_h-2, 0xff) */
-			if (mono_current_par.bplcon0 & BPC0_SHRES)
-				/*
-				 * htotal-4 is used in SHRES-mode, as diwstop_h-2 doesn't work under ECS.
-				 * Does this work under AGA?
-				 * -Jes
-				 */
-				*cop++ = 0xff00 | ((mono_current_par.htotal-4) | 1);
-			else 
-				*cop++ = 0xff00 | ((diwstop_h-2) >> 1) | 0x1;
-
-			*cop++ = 0xfffe;
-			/* Wait(0, 0) - make sure we're in the new segment */
-			*cop++ = 0x0001;
-			*cop++ = 0xfffe;
-			line -= 256;
+	/*
+	 * Pixel modes for Bitplanes and Sprites
+	 */
 
-			/*
-			 * Under ECS we have to keep color[0], as it is part of a special color-table.
-			 */
+static u_short bplpixmode[3] = {
+	BPC0_SHRES,			/*  35 ns */
+	BPC0_HIRES,			/*  70 ns */
+	0				/* 140 ns */
+};
 
-			if (boot_info.bi_amiga.chipset == CS_ECS && mono_current_par.bplcon0 & BPC0_ECSENA) {
-				*cop++ = 0x0180;
-				*cop++ = mono_ecs_color_zero;
-			}
-		}
-	
-		/* Wait(diwstop_h, line - 1) */
-		*cop++ = ((line - 1)   << 8) | (diwstop_h >> 1) | 0x1;
-		*cop++ = 0xfffe;
-
-		for (i = 0 ; i < mono_current_par.scr_depth ; i++) {
-			scrmem = (long)mono_current_par.bitplane[i];
-			if (shf)
-				scrmem += mono_current_par.bytes_per_row;
-
-				*cop++ = CUSTOM_OFS(bplpt[i]); 
-				*cop++ = (long)scrmem >> 16;
-				*cop++ = CUSTOM_OFS(bplpt[i]) + 2;
-				*cop++ = (long)scrmem;
-		}
-	}
-    
-	/* End of Copper list */
-	*cop++ = 0xffff;
-	*cop++ = 0xfffe;
+static u_short sprpixmode[3] = {
+	BPC3_SPRES1 | BPC3_SPRES0,	/*  35 ns */
+	BPC3_SPRES1,			/*  70 ns */
+	BPC3_SPRES0			/* 140 ns */
+};
 
-	return(cop);
-}
+	/*
+	 * Fetch modes for Bitplanes and Sprites
+	 */
 
+static u_short bplfetchmode[3] = {
+	0,				/* 1x */
+	FMODE_BPL32,			/* 2x */
+	FMODE_BPAGEM | FMODE_BPL32	/* 4x */
+};
 
-static __inline__ void mono_build_cursor(register struct display *p)
-{
-	int vs, hs, ve;
-	ushort diwstrt_v = mono_current_par.diwstrt_v;
-	ushort diwstrt_h = mono_current_par.diwstrt_h;
+static u_short sprfetchmode[3] = {
+	0,				/* 1x */
+	FMODE_SPR32,			/* 2x */
+	FMODE_SPAGEM | FMODE_SPR32	/* 4x */
+};
 
-	if (mono_current_par.bplcon0 & BPC0_LACE) {
-		vs = diwstrt_v + (p->cursor_y * p->fontheight)/2; 
-		ve = vs + p->fontheight/2;
-	} else {
-		vs = diwstrt_v + (p->cursor_y * p->fontheight); 
-		ve = vs + p->fontheight;
-	}
+	/*
+	 * Default Colormaps
+	 */
 
-	if (mono_current_par.bplcon0 & BPC0_ECSENA)
-		/*
-		 * It's an AGA mode. We'll assume that the sprite was set
-		 * into 35ns resolution by the appropriate SPRES bits in bplcon3.
-		 */
-		hs = diwstrt_h  * 4 + (p->cursor_x * p->fontwidth) - 4;
-	else
-		hs = diwstrt_h + (p->cursor_x * p->fontwidth) / 2 - 1;
+static u_short red2[] =
+	{ 0x0000, 0xc000 };
+static u_short green2[] =
+	{ 0x0000, 0xc000 };
+static u_short blue2[] =
+	{ 0x0000, 0xc000 };
 
-	if (mono_current_par.bplcon0 & BPC0_ECSENA) {
-		/* There are some high-order bits on the sprite position */
-		*((ulong *) mono_current_par.cursor) =
-		((((vs & 0xff) << 24) | ((vs & 0x100) >> 6) |
-		((vs & 0x200) >> 3)) |
-		(((hs & 0x7f8) << 13) | ((hs & 0x4) >> 2) |
-		((hs & 0x3) << 3)) |
-		(((ve & 0xff) << 8) | ((ve & 0x100) >> 7) |
-		((ve & 0x200) >> 4)));
-	} else {
-		*((ulong *) mono_current_par.cursor) =
-		((vs << 24) | ((vs & 0x00000100) >> 6) |
-		((hs & 0x000001fe) << 15) | (hs & 0x00000001) | 
-		((ve & 0x000000ff) << 8) | ((ve & 0x00000100) >> 7));
-	}
-}
+static u_short red4[] =
+	{ 0x0000, 0xc000, 0x8000, 0xffff };
+static u_short green4[] =
+	{ 0x0000, 0xc000, 0x8000, 0xffff };
+static u_short blue4[] =
+	{ 0x0000, 0xc000, 0x8000, 0xffff };
 
-static void mono_build_ecs_colors(ushort color1, ushort color2, ushort color3, 
-                      ushort color4, ushort *table)
-{
-/*
- * This function calculates the special ECS color-tables needed when running
- * new screen-modes available under ECS. See the hardware reference manual
- * 3rd edition for details.
- * -Jes
- */
-ushort  t;
+static u_short red8[] =
+	{ 0x0000, 0x0000, 0x0000, 0x0000, 0xc000, 0xc000, 0xc000, 0xc000 };
+static u_short green8[] =
+	{ 0x0000, 0x0000, 0xc000, 0xc000, 0x0000, 0x0000, 0xc000, 0xc000 };
+static u_short blue8[] =
+	{ 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000 };
 
-        t = (color1 & 0x0ccc);
-        table[0] = t;
-        table[4] = t;
-        table[8] = t;
-        table[12] = t;
-        t = t >> 2;
-        table[0] = (table[0] | t);
-        table[1] = t;
-        table[2] = t;
-        table[3] = t;
-
-        t = (color2 & 0x0ccc);
-        table[1] = (table[1] | t);
-        table[5] = t;
-        table[9] = t;
-        table[13] = t;
-        t = t >> 2;
-        table[4] = (table[4] | t);
-        table[5] = (table[5] | t);
-        table[6] = t;
-        table[7] = t;
-
-        t = (color3 & 0x0ccc);
-        table[2] = (table[2] | t);
-        table[6] = (table[6] | t);
-        table[10] = t;
-        table[14] = t;
-        t = t >> 2;
-        table[8] = (table[8] | t);
-        table[9] = (table[9] | t);
-        table[10] = (table[10] | t);
-        table[11] = t;
-
-        t = (color4 & 0x0ccc);
-        table[3] = (table[3] | t);
-        table[7] = (table[7] | t);
-        table[11] = (table[11] | t);
-        table[15] = t;
-        t = t >> 2;
-        table[12] = (table[12] | t);
-        table[13] = (table[13] | t);
-        table[14] = (table[14] | t);
-        table[15] = (table[15] | t);
+static u_short red16[] =
+	{ 0x0000, 0x0000, 0x0000, 0x0000, 0xc000, 0xc000, 0xc000, 0xc000,
+	  0x8000, 0x0000, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff };
+static u_short green16[] =
+	{ 0x0000, 0x0000, 0xc000, 0xc000, 0x0000, 0x0000, 0xc000, 0xc000,
+	  0x8000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff };
+static u_short blue16[] =
+	{ 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000,
+	  0x8000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff };
 
-}
 
-/* mono_display_init():
- *
- *    Fills out (struct display *) given a geometry structure
- */
+static struct fb_cmap default_2_colors =
+	{ 0, 2, red2, green2, blue2, NULL };
+static struct fb_cmap default_8_colors =
+	{ 0, 8, red8, green8, blue8, NULL };
+static struct fb_cmap default_4_colors =
+	{ 0, 4, red4, green4, blue4, NULL };
+static struct fb_cmap default_16_colors =
+	{ 0, 16, red16, green16, blue16, NULL };
 
-static void mono_display_init(struct display *p,
-                         struct geometry *geom, ushort inverse)
-{
-	ushort ecs_table[16];
-	int    i;
-	char   *chipptr;
-	ushort diwstrt_v, diwstop_v;
-	ushort diwstrt_h, diwstop_h;
-	ushort diw_min_h, diw_min_v;
-	ushort bplmod, diwstrt, diwstop, diwhigh, ddfstrt, ddfstop;
-	ushort cursorheight, cursormask = 0;
-	u_long size;
-
-	/* Decide colour scheme */
-
-	if (inverse) {
-		mono_current_par.fgcol   = FG_COLOR_INV;
-		mono_current_par.bgcol   = BG_COLOR_INV;
-		mono_current_par.crsrcol = CRSR_COLOR_INV;
-	} else {
-		mono_current_par.fgcol   = FG_COLOR;
-		mono_current_par.bgcol   = BG_COLOR;
-		mono_current_par.crsrcol = CRSR_COLOR;
-	}
-
-	/* Define screen geometry */
-   
-	mono_current_par.scr_max_height = geom->scr_max_height;
-	mono_current_par.scr_max_width  = geom->scr_max_width; 
-	mono_current_par.scr_height     = geom->scr_height;
-	mono_current_par.scr_width      = geom->scr_width;
-	mono_current_par.scr_depth      = geom->scr_depth;
-	mono_current_par.bplcon0        = geom->bplcon0 | BPC0_COLOR;
-	mono_current_par.htotal         = geom->htotal;
+	/*
+	 * Interface used by the world
+	 */
 
-	/* htotal was added, as I use it to calc the pal-line. -Jes */
+void amiga_video_setup(char *options, int *ints);
 
-	if (mono_current_par.scr_depth < 8)
-		mono_current_par.bplcon0 |= (mono_current_par.scr_depth << 12);
-	else {
-		/* must be exactly 8 */
-		mono_current_par.bplcon0 |= BPC0_BPU3;
-	}
+static int amiga_fb_get_fix(struct fb_fix_screeninfo *fix, int con);
+static int amiga_fb_get_var(struct fb_var_screeninfo *var, int con);
+static int amiga_fb_set_var(struct fb_var_screeninfo *var, int con);
+static int amiga_fb_pan_display(struct fb_var_screeninfo *var, int con);
+static int amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con);
+static int amiga_fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+                          u_long arg, int con);
 
-	diw_min_v         = geom->diwstrt_v;
-	diw_min_h         = geom->diwstrt_h;
+static int amiga_fb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con);
+static int amiga_fb_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con);
+static int amiga_fb_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con);
+static int amiga_fb_get_cursorstate(struct fb_cursorstate *state, int con);
+static int amiga_fb_set_cursorstate(struct fb_cursorstate *state, int con);
 
-	/* We can derive everything else from this, at least for OCS */
 	/*
-	 * For AGA: we don't use the finer position control available for
-	 * diw* yet (could be set by 35ns increments).
+	 * Interface to the low level console driver
 	 */
 
-	/* Calculate line and plane size while respecting the alignment restrictions */
-	mono_current_par.bytes_per_row  = ((mono_current_par.scr_width+geom->alignment-1)&~(geom->alignment-1)) >> 3;
-	mono_current_par.plane_size     = mono_current_par.bytes_per_row * mono_current_par.scr_height;
-
+struct fb_info *amiga_fb_init(long *mem_start);
+static int amifbcon_switch(int con);
+static int amifbcon_updatevar(int con);
+static void amifbcon_blank(int blank);
 
 	/*
-	 *		Quick hack for frame buffer mmap():
-	 *
-	 *		plane_size must be a multiple of the page size
+	 * Internal routines
 	 */
 
-	mono_current_par.plane_size = PAGE_ALIGN(mono_current_par.plane_size);
-
-
-	mono_current_par.y_wrap   = 0;                  mono_current_par.scroll_latch = 1;
-	p->cursor_x = 0; p->cursor_y = 0; mono_current_par.cursor_latch = 1;
+static struct fb_cmap *get_default_colormap(int bpp);
+static int do_fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var,
+                          int kspc);
+static int do_fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var,
+                          int kspc);
+static void do_install_cmap(int con);
+static void memcpy_fs(int fsfromto, void *to, void *from, int len);
+static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto);
+static int alloc_cmap(struct fb_cmap *cmap, int len, int transp);
+static int flash_cursor(void);
+static void amifb_interrupt(int irq, void *dev_id, struct pt_regs *fp);
+static void get_video_mode(const char *name);
+static void check_default_mode(void);
+static u_long chipalloc(u_long size);
+static char *strtoke(char *s,const char *ct);
 
-	if (mono_current_par.bplcon0 & BPC0_LACE) {
-		bplmod = mono_current_par.bytes_per_row;
-		diwstrt_v = diw_min_v + (mono_current_par.scr_max_height - mono_current_par.scr_height)/4;
-		diwstop_v = (diwstrt_v + mono_current_par.scr_height/2);
-	} else {
-		bplmod = 0;
-		diwstrt_v = diw_min_v + (mono_current_par.scr_max_height - mono_current_par.scr_height)/2;
-		diwstop_v = (diwstrt_v + mono_current_par.scr_height);
-	}
+	/*
+	 * Hardware routines
+	 */
 
-	if (mono_current_par.bplcon0 & BPC0_HIRES) {
-		diwstrt_h =  diw_min_h + (mono_current_par.scr_max_width - mono_current_par.scr_width)/4;
-		diwstop_h = (diwstrt_h + mono_current_par.scr_width/2);
-		/* ??? Where did 0x1d5 come from in original code ??? */
-	} else if (mono_current_par.bplcon0 & BPC0_SHRES) {
-		diwstrt_h =  diw_min_h + (mono_current_par.scr_max_width - mono_current_par.scr_width)/8;
-		diwstop_h = (diwstrt_h + mono_current_par.scr_width/4);
-	} else {
-		diwstrt_h =  diw_min_h + (mono_current_par.scr_max_width - mono_current_par.scr_width)/2;
-		diwstop_h = (diwstrt_h + mono_current_par.scr_width);
-	}
+static int ami_encode_fix(struct fb_fix_screeninfo *fix,
+                          struct amiga_fb_par *par);
+static int ami_decode_var(struct fb_var_screeninfo *var,
+                          struct amiga_fb_par *par);
+static int ami_encode_var(struct fb_var_screeninfo *var,
+                          struct amiga_fb_par *par);
+static void ami_get_par(struct amiga_fb_par *par);
+static void ami_set_var(struct fb_var_screeninfo *var);
+#ifdef DEBUG
+static void ami_set_par(struct amiga_fb_par *par);
+#endif
+static void ami_pan_var(struct fb_var_screeninfo *var);
+static int ami_update_par(void);
+static int ami_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+                         u_int *transp);
+static int ami_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                         u_int transp);
+static void ami_update_display(void);
+static void ami_init_display(void);
+static void ami_do_blank(void);
+static int ami_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con);
+static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con);
+static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con);
+static int ami_get_cursorstate(struct fb_cursorstate *state, int con);
+static int ami_set_cursorstate(struct fb_cursorstate *state, int con);
+static void ami_set_sprite(void);
+static void ami_init_copper(void);
+static void ami_reinit_copper(void);
+static void ami_build_copper(void);
+static void ami_rebuild_copper(void);
 
-	if (mono_current_par.bplcon0 & BPC0_HIRES) {
-		ddfstrt = (diwstrt_h >> 1) - 4;
-		ddfstop = ddfstrt + (4 * (mono_current_par.bytes_per_row>>1)) - 8;
-	} else if (mono_current_par.bplcon0 & BPC0_SHRES && boot_info.bi_amiga.chipset == CS_AGA) {
-		/* There may be some interaction with FMODE here... -8 is magic. */
 
-		/*
-		 * This should be fixed, so it supports all different
-		 * FMODE's.  FMODE varies the speed with 1,2 & 4 the
-		 * standard ECS speed.  Someone else has to do it, as
-		 * I don't have an AGA machine with MMU available
-		 * here.
-		 *
-		 * This particular speed looks like FMODE = 3 to me.
-		 * ddfstop should be changed so it depends on FMODE under AGA.
-		 * -Jes
-		 */
-		ddfstrt = (diwstrt_h >> 1) - 8;
-		ddfstop = ddfstrt + (2 * (mono_current_par.bytes_per_row>>1)) - 8;
-	} else if (mono_current_par.bplcon0 & BPC0_SHRES && boot_info.bi_amiga.chipset == CS_ECS){
-		/* 
-		 * Normal speed for ECS, should be the same for FMODE = 0
-		 * -Jes
-		 */
-		ddfstrt = (diwstrt_h >> 1) - 2;
-		ddfstop = ddfstrt + (2 * (mono_current_par.bytes_per_row>>1)) - 8;
-	} else {
-		ddfstrt = (diwstrt_h >> 1) - 8;
-		ddfstop = ddfstrt + (8 * (mono_current_par.bytes_per_row>>1)) - 8;
-	}
+	/*
+	 * External references
+	 */
 
-	if (mono_current_par.bplcon0 & BPC0_LACE)
-		cursorheight = p->fontheight/2;
-	else
-		cursorheight = p->fontheight;
+extern unsigned short ami_intena_vals[];
 
 	/*
-	 *		Quick hack for frame buffer mmap():
-	 *
-	 *		chipptr must be at a page boundary
+	 * Support for Graphics Boards
 	 */
 
-	size = mono_current_par.scr_depth*mono_current_par.plane_size+COP_MEM_REQ+SPR_MEM_REQ+4*(cursorheight-1);
-	size += PAGE_SIZE-1;
-	chipptr = amiga_chip_alloc(size);
-	chipptr = (char *)PAGE_ALIGN((u_long)chipptr);
+#ifdef CONFIG_FB_CYBER			/* Cybervision */
+extern int Cyber_probe(void);
+extern void Cyber_video_setup(char *options, int *ints);
+extern struct fb_info *Cyber_fb_init(long *mem_start);
 
-   
-	/* locate the bitplanes */
-	/* These MUST be 64 bit aligned for full AGA compatibility!! */
+static int amifb_Cyber = 0;
+#endif /* CONFIG_FB_CYBER */
 
-	mono_current_par.smem_start = (u_long)chipptr;
-	mono_current_par.smem_len = mono_current_par.plane_size*mono_current_par.scr_depth;
-	mono_current_par.geometry = geom;
+#ifdef CONFIG_GSP_RESOLVER			/* DMI Resolver */
+extern int resolver_probe(void);
+extern void resolver_video_setup(char *options, int *ints);
+extern struct fb_info *resolver_fb_init(long *mem_start);
 
-	for (i = 0 ; i < mono_current_par.scr_depth ; i++, chipptr += mono_current_par.plane_size) {
-		mono_current_par.bitplane[i] = (u_char *) chipptr;
-		memset ((void *)chipptr, 0, mono_current_par.plane_size);  /* and clear */
-	}
+static int amifb_resolver = 0;
+#endif /* CONFIG_GSP_RESOLVER */
 
-	/* locate the copper lists */
-	mono_current_par.coplist1hdr = (ushort *) chipptr;  chipptr += MAX_COP_LIST_ENTS * 4;
-	mono_current_par.coplist2hdr = (ushort *) chipptr;  chipptr += MAX_COP_LIST_ENTS * 4;
+static struct fb_ops amiga_fb_ops = {
+	amiga_fb_get_fix, amiga_fb_get_var, amiga_fb_set_var, amiga_fb_get_cmap,
+	amiga_fb_set_cmap, amiga_fb_pan_display, amiga_fb_ioctl
+};
 
-	/* locate the sprite data */
-	mono_current_par.cursor      = (ushort *) chipptr;  chipptr += 8+4*cursorheight;
-	mono_current_par.dummy       = (ushort *) chipptr;  chipptr += 12;
+void amiga_video_setup(char *options, int *ints)
+{
+	char *this_opt;
+	int i;
+	char mcap_spec[80];
 
-	/* create the sprite data for the cursor image */
-	memset((void *)mono_current_par.cursor, 0, 8+4*cursorheight);
 	/*
-	 * Only AGA supplies hires sprites.
+	 * Check for a Graphics Board
 	 */
-	if (mono_current_par.bplcon0 & BPC0_ECSENA && boot_info.bi_amiga.chipset == CS_AGA)
-		/* AGA cursor is SHIRES, ECS sprites differ */
-		for (i = 0; (i < p->fontwidth) && (i < 16); i++)
-			cursormask |= 1<<(15-i);
-	else
-		/* For OCS & ECS sprites are pure LORES 8-< */
-		for (i = 0; (i < p->fontwidth/2) && (i < 8); i++)
-			cursormask |= 1<<(15-i);
-
-	mono_current_par.cursor[0] = mono_cursor_data[0];
-	mono_current_par.cursor[1] = mono_cursor_data[1];
 
-#if (CRSR_BLOCK == 1)
-	for (i = 0; i < cursorheight; i++)
-#else
-	for (i = cursorheight-2; i < cursorheight; i++)
-#endif
-		mono_current_par.cursor[2+2*i] = cursormask;
-
-	/* set dummy sprite data to a blank sprite */
-	memset((void *)mono_current_par.dummy, 0, 12);
-  
-	/* set cursor flashing */
-	mono_current_par.cursor_flash = CRSR_FLASH;
-
-	/* Make the cursor invisible */
-	mono_current_par.cursor_visible = 0;
- 
-	/* Initialise the chipregs */
-	mono_current_par.diwstrt_v = diwstrt_v;
-	mono_current_par.diwstrt_h = diwstrt_h;
-	mono_current_par.diwstop_v = diwstop_v;
-	mono_current_par.diwstop_h = diwstop_h;
-	diwstrt = ((diwstrt_v << 8) | diwstrt_h);
-	diwstop = ((diwstop_v & 0xff) << 8) | (diwstop_h & 0xff);
-
-	custom.bplcon0   = mono_current_par.bplcon0;	/* set the display mode */
-	custom.bplcon1   = 0;		/* Needed for horizontal scrolling */
-	custom.bplcon2   = 0;
-	custom.bpl1mod   = bplmod;
-	custom.bpl2mod   = bplmod;
-	custom.diwstrt   = diwstrt;
-	custom.diwstop   = diwstop;
-	custom.ddfstrt   = ddfstrt;
-	custom.ddfstop   = ddfstop;
-
-	custom.color[0]  = COLOR_MSB(mono_current_par.bgcol);
-	custom.color[1]  = COLOR_MSB(mono_current_par.fgcol);
-	custom.color[17] = COLOR_MSB(mono_current_par.crsrcol); /* Sprite 0 color */
-
-	if (boot_info.bi_amiga.chipset == CS_AGA) {
-		/* Fill in the LSB of the 24 bit color palette */
-		/* Must happen after MSB */
-		custom.bplcon3   = geom->bplcon3 | BPC3_LOCT;
-		custom.color[0]  = COLOR_LSB(mono_current_par.bgcol);
-		custom.color[1]  = COLOR_LSB(mono_current_par.fgcol);
-		custom.color[17] = COLOR_LSB(mono_current_par.crsrcol);
-		custom.bplcon3   = geom->bplcon3;
-	}
-
-	if (boot_info.bi_amiga.chipset == CS_ECS && mono_current_par.bplcon0 & BPC0_ECSENA) {
-		/*
-		 * Calculation of the special ECS color-tables for
-		 * planes and sprites is done in the function
-		 * build_ecs_table
-		 */
-
-		/*
-		 * Calcs a special ECS colortable for the bitplane,
-		 * and copies it to the custom registers
-		 */
-		mono_build_ecs_colors(COLOR_MSB(mono_current_par.bgcol), COLOR_MSB(mono_current_par.fgcol),
-				 0, 0, ecs_table); 
-
-#if 0
-		for (i = 0; i < 8; i++){
-			custom.color[i]   = ecs_table[i*2];
-			custom.color[i+8] = ecs_table[i*2+1];
+#ifdef CONFIG_FB_CYBER
+	if (options && *options)
+		if (!strncmp(options, "cyber", 5) && Cyber_probe()) {
+			amifb_Cyber = 1;
+			Cyber_video_setup(options, ints);
+			return;
 		}
-#else
-		for (i = 0; i < 16; i++){
-			custom.color[i]   = ecs_table[i];
+#endif /* CONFIG_FB_CYBER */
+#ifdef CONFIG_GSP_RESOLVER
+	if (options && *options)
+		if (!strncmp(options, "resolver", 5) && resolver_probe()) {
+			amifb_resolver = 1;
+			resolver_video_setup(options, ints);
+			return;
 		}
 #endif
 
-		mono_ecs_color_zero = ecs_table[0];
-
-		/*
-		 * Calcs a special ECS colortable for the cursor
-		 * sprite, and copies it to the appropriate custom
-		 * registers
-		 */
-		mono_build_ecs_colors(0, COLOR_MSB(mono_current_par.crsrcol), 0, 0, ecs_table);
-
-		for (i = 0; i < 16; i++){
-			custom.color[i+16] = ecs_table[i];
-		}
-	}
-
-	if (!(geom->isOCS)) {
-		/* Need to set up a bunch more regs */
-		/* Assumes that diwstrt is in the (0,0) sector, but stop might not be */
-		diwhigh = (diwstop_v & 0x700) | ((diwstop_h & 0x100) << 5);
-
-		custom.bplcon2   = geom->bplcon2;
-		custom.bplcon3   = geom->bplcon3;
-		/* save bplcon3 for blanking */
-		mono_save_bplcon3 = geom->bplcon3;
-
-		custom.diwhigh   = diwhigh;	/* must happen AFTER diwstrt, stop */
-
-		custom.htotal	= geom->htotal;
-		custom.hsstrt	= geom->hsstrt;
-		custom.hsstop	= geom->hsstop;
-		custom.hbstrt	= geom->hbstrt;
-		custom.hbstop	= geom->hbstop;
-		custom.vtotal	= geom->vtotal;
-		custom.vsstrt	= geom->vsstrt;
-		custom.vsstop	= geom->vsstop;
-		custom.vbstrt	= geom->vbstrt;
-		custom.vbstop	= geom->vbstop;
-		custom.hcenter	= geom->hcenter;
-		custom.beamcon0	= geom->beamcon0;
-		if (boot_info.bi_amiga.chipset == CS_AGA) {
-			custom.fmode	= geom->fmode;
-		}
-		/* 
-		 * fmode does NOT! exist under ECS, weird things might happen
-		 */
-
-		/* We could load 8-bit colors here, if we wanted */
-
-		/*
-		 *    The minimum period for audio depends on htotal (for OCS/ECS/AGA)
-		 */
-		if (boot_info.bi_amiga.chipset != CS_STONEAGE)
-			amiga_audio_min_period = (geom->htotal>>1)+1;
-	}
-
-
-	/* Build initial copper lists. sprites must be set up, and mono_current_par.diwstrt. */
-
-	if (mono_current_par.bplcon0 & BPC0_LACE) {
-		mono_current_par.coplist1dyn = mono_build_clist_hdr(p,mono_current_par.coplist1hdr, mono_current_par.coplist2hdr),
-		mono_build_clist_dyn(p, mono_current_par.coplist1dyn, 0);
-
-		mono_current_par.coplist2dyn = mono_build_clist_hdr(p,mono_current_par.coplist2hdr, mono_current_par.coplist1hdr),
-		mono_build_clist_dyn(p, mono_current_par.coplist2dyn, 1);
-	} else {
-		mono_current_par.coplist1dyn = mono_build_clist_hdr(p,mono_current_par.coplist1hdr, NULL),
-		mono_build_clist_dyn(p, mono_current_par.coplist1dyn, 0);
-	}
-
-
-	/* Get ready to run first copper list */
-	custom.cop1lc = mono_current_par.coplist1hdr;
-	custom.copjmp1 = 0;
-
-	/* turn on DMA for bitplane and sprites */
-	custom.dmacon = DMAF_SETCLR | DMAF_RASTER | DMAF_COPPER | DMAF_SPRITE;
-
-	if (mono_current_par.bplcon0 & BPC0_LACE) {
-		/* Make sure we get the fields in the right order */
+	mcap_spec[0] = '\0';
+	fb_info.fontname[0] = '\0';
 
-		/* wait for LOF frame bit to go low */
-		while (custom.vposr & 0x8000)
-			;
+	if (!options || !*options)
+		return;
 
-		/* wait for LOF frame bit to go high */
-		while (!(custom.vposr & 0x8000))
-			;
+	for (this_opt = strtok(options, ","); this_opt; this_opt = strtok(NULL, ",")) {
+		char *p;
 
-		/* start again at the beginning of copper list 1 */
-		custom.cop1lc = mono_current_par.coplist1hdr;
-		custom.copjmp1 = 0;
+		if (!strcmp(this_opt, "inverse")) {
+			amifb_inverse = 1;
+			for (i = 0; i < 16; i++) {
+				red16[i] = ~red16[i];
+				green16[i] = ~green16[i];
+				blue16[i] = ~blue16[i];
+			}
+			for (i = 0; i < 8; i++) {
+				red8[i] = ~red8[i];
+				green8[i] = ~green8[i];
+				blue8[i] = ~blue8[i];
+			}
+			for (i = 0; i < 4; i++) {
+				red4[i] = ~red4[i];
+				green4[i] = ~green4[i];
+				blue4[i] = ~blue4[i];
+			}
+			for (i = 0; i < 2; i++) {
+				red2[i] = ~red2[i];
+				green2[i] = ~green2[i];
+				blue2[i] = ~blue2[i];
+			}
+		} else if (!strcmp(this_opt, "ilbm"))
+			amifb_ilbm = 1;
+		else if (!strncmp(this_opt, "monitorcap:", 11))
+			strcpy(mcap_spec, this_opt+11);
+		else if (!strncmp(this_opt, "font:", 5))
+			strcpy(fb_info.fontname, this_opt+5);
+		else if (!strncmp(this_opt, "fstart:", 7))
+			min_fstrt = simple_strtoul(this_opt+7, NULL, 0);
+		else if (!strncmp(this_opt, "depth:", 6))
+			amiga_fb_predefined[0].bits_per_pixel =
+				simple_strtoul(this_opt+6, NULL, 0);
+		else if (!strncmp(this_opt, "size:", 5)) {
+			p = this_opt + 5;
+			if (*p != ';')
+				amiga_fb_predefined[0].xres =
+					simple_strtoul(p, NULL, 0);
+			if (!(p = strchr(p, ';')))
+				continue;
+			if (*++p != ';')
+				amiga_fb_predefined[0].yres =
+					simple_strtoul(p, NULL, 0);
+			if (!(p = strchr(p, ';')))
+				continue;
+			if (*++p != ';')
+				amiga_fb_predefined[0].xres_virtual =
+					simple_strtoul(p, NULL, 0);
+			if (!(p = strchr(p, ';')))
+				continue;
+			if (*++p != ';')
+				amiga_fb_predefined[0].yres_virtual =
+					simple_strtoul(p, NULL, 0);
+			if (!(p = strchr(p, ';')))
+				continue;
+			if (*++p)
+				amiga_fb_predefined[0].bits_per_pixel =
+					simple_strtoul(p, NULL, 0);
+		} else if (!strncmp(this_opt, "timing:", 7)) {
+			p = this_opt + 7;
+			if (*p != ';')
+				amiga_fb_predefined[0].left_margin =
+					simple_strtoul(p, NULL, 0);
+			if (!(p = strchr(p, ';')))
+				continue;
+			if (*++p != ';')
+				amiga_fb_predefined[0].right_margin =
+					simple_strtoul(p, NULL, 0);
+			if (!(p = strchr(p, ';')))
+				continue;
+			if (*++p != ';')
+				amiga_fb_predefined[0].upper_margin =
+					simple_strtoul(p, NULL, 0);
+			if (!(p = strchr(p, ';')))
+				continue;
+			if (*++p)
+				amiga_fb_predefined[0].lower_margin =
+					simple_strtoul(p, NULL, 0);
+		} else if (!strncmp(this_opt, "sync:", 5)) {
+			p = this_opt + 5;
+			if (*p != ';')
+				amiga_fb_predefined[0].hsync_len =
+					simple_strtoul(p, NULL, 0);
+			if (!(p = strchr(p, ';')))
+				continue;
+			if (*++p)
+				amiga_fb_predefined[0].vsync_len =
+					simple_strtoul(p, NULL, 0);
+		} else
+			get_video_mode(this_opt);
 	}
-}
 
+	if (min_fstrt < 48)
+		min_fstrt = 48;
 
-static void mono_amifb_interrupt(int irq, struct pt_regs *fp, void *data)
-{
-	register struct display *p = disp;
-
-	static ushort cursorcount = 0;
-	static ushort cursorstate = 0;
+	if (*mcap_spec) {
+		char *p;
+		int vmin, vmax, hmin, hmax;
 
-	/* I *think* that you should only change display lists on long frame.
-	 * At least it goes awfully peculiar on my A500 without the following
-	 * test. Not really in a position to test this hypothesis, so sorry
-	 * for the slow scrolling, all you flicker-fixed souls
+	/* Format for monitor capabilities is: <Vmin>;<Vmax>;<Hmin>;<Hmax>
+	 * <V*> vertical freq. in Hz
+	 * <H*> horizontal freq. in kHz
 	 */
 
-	if (!(mono_current_par.bplcon0 & BPC0_LACE) || (custom.vposr & 0x8000)) {
-		if (mono_current_par.scroll_latch || mono_current_par.cursor_latch)
-			mono_build_cursor(p);
-
-		if (mono_current_par.scroll_latch)
-			if (mono_current_par.bplcon0 & BPC0_LACE) {
-				mono_build_clist_dyn(p, mono_current_par.coplist1dyn, 0);
-				mono_build_clist_dyn(p, mono_current_par.coplist2dyn, 1);
-			} else
-				mono_build_clist_dyn(p, mono_current_par.coplist1dyn, 0);
-			mono_current_par.scroll_latch = 0;
-			mono_current_par.cursor_latch = 0;
-	}
-
-	if (!(custom.potgor & (1<<10)))
-		mono_vblank.right_count++;
-
-	if (mono_current_par.cursor_visible) {
-		if (mono_current_par.cursor_flash) {
-			if (cursorcount)
-				cursorcount--;
-			else {
-				cursorcount = CRSR_RATE;
-				if ((cursorstate = !cursorstate))
-					custom.dmacon = DMAF_SETCLR | DMAF_SPRITE;
-				else
-					custom.dmacon = DMAF_SPRITE;
-			}
-		}
-	} else
-		custom.dmacon = DMAF_SPRITE;
-
-	if (mono_do_blank) {
-		custom.dmacon = DMAF_RASTER | DMAF_SPRITE;
-		custom.color[0] = 0;
-		if (boot_info.bi_amiga.chipset == CS_AGA) {
-			/* Fill in the LSB of the 24 bit color palette */
-			/* Must happen after MSB */
-			custom.bplcon3 = mono_save_bplcon3 | BPC3_LOCT;
-			custom.color[0]= 0;
-			custom.bplcon3 = mono_save_bplcon3;
-		}
-		mono_do_blank = 0;
-	}
-
-	if (mono_do_unblank) {
-		if (mono_current_par.cursor_visible)
-			custom.dmacon = DMAF_SETCLR | DMAF_RASTER | DMAF_SPRITE;
-		else
-			custom.dmacon = DMAF_SETCLR | DMAF_RASTER;
-		custom.color[0] = COLOR_MSB(mono_current_par.bgcol);
-		if (boot_info.bi_amiga.chipset == CS_AGA) {
-			/* Fill in the LSB of the 24 bit color palette */
-			/* Must happen after MSB */
-			custom.bplcon3 = mono_save_bplcon3 | BPC3_LOCT;
-			custom.color[0] = COLOR_LSB(mono_current_par.bgcol);
-			custom.bplcon3 = mono_save_bplcon3;
-		}
-		/* color[0] is set to mono_ecs_color_zero under ECS */
-		if (boot_info.bi_amiga.chipset == CS_ECS && mono_current_par.bplcon0 & BPC0_ECSENA) {
-			custom.color[0]  = mono_ecs_color_zero;
-		}
-		mono_do_unblank = 0;
+		if (!(p = strtoke(mcap_spec, ";")) || !*p)
+			goto cap_invalid;
+		vmin = simple_strtoul(p, NULL, 10);
+		if (vmin <= 0)
+			goto cap_invalid;
+		if (!(p = strtoke(NULL, ";")) || !*p)
+			goto cap_invalid;
+		vmax = simple_strtoul(p, NULL, 10);
+		if (vmax <= 0 || vmax <= vmin)
+			goto cap_invalid;
+		if (!(p = strtoke(NULL, ";")) || !*p)
+			goto cap_invalid;
+		hmin = 1000 * simple_strtoul(p, NULL, 10);
+		if (hmin <= 0)
+			goto cap_invalid;
+		if (!(p = strtoke(NULL, "")) || !*p)
+			goto cap_invalid;
+		hmax = 1000 * simple_strtoul(p, NULL, 10);
+		if (hmax <= 0 || hmax <= hmin)
+			goto cap_invalid;
+
+		vfmin = vmin;
+		vfmax = vmax;
+		hfmin = hmin;
+		hfmax = hmax;
+cap_invalid:
+		;
 	}
-
-	mono_vblank.done = 1;
 }
 
-
-static int mono_amiga_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
-{
-	int i;
-
-	strcpy(fix->id, mono_current_par.geometry->modename);
-	fix->smem_start = mono_current_par.smem_start;
-	fix->smem_len = mono_current_par.smem_len;
-
 	/*
-	 *		Only monochrome bitmap at the moment
+	 * Get the Fixed Part of the Display
 	 */
 
-	fix->type = FB_TYPE_PACKED_PIXELS;
-
-	fix->type_aux = 0;
-	if (mono_amifb_inverse)
-		fix->visual = FB_VISUAL_MONO10;
-	else
-		fix->visual = FB_VISUAL_MONO01;
-
-	fix->xpanstep = 0;
-	fix->ypanstep = 0;
-	fix->ywrapstep = 1;
-
-	fix->line_length = 0;
-	for (i = 0; i < arraysize(fix->reserved); i++)
-		fix->reserved[i] = 0;
-	return(0);
-}
-
-
-static int mono_amiga_fb_get_var(struct fb_var_screeninfo *var, int con)
+static int amiga_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
 {
-	int i;
-
-	var->xres = mono_current_par.geometry->scr_width;
-	var->yres = mono_current_par.geometry->scr_height;
-	var->xres_virtual = var->xres;
-	var->yres_virtual = var->yres;
-	var->xoffset = 0;
-	var->yoffset = 0;
+	struct amiga_fb_par par;
 
-	var->bits_per_pixel = mono_current_par.geometry->scr_depth;
-	var->grayscale = 0;
+	if (con == -1)
+		ami_get_par(&par);
+	else {
+		int err;
 
-	if (boot_info.bi_amiga.chipset == CS_AGA) {
-		var->red.offset = 0;
-		var->red.length = 8;
-		var->red.msb_right = 0;
-		var->green = var->red;
-		var->blue = var->red;
-	} else {
-		var->red.offset = 0;
-		var->red.length = 4;
-		var->red.msb_right = 0;
-		var->green = var->red;
-		var->blue = var->red;
+		if ((err = ami_decode_var(&disp[con].var, &par)))
+			return err;
 	}
-
-	var->nonstd = 0;
-	var->activate = 0;
-
-	var->width = -1;
-	var->height = -1;
-
-	var->accel = FB_ACCEL_NONE;
-
-	var->pixclock = 35242;
-	var->left_margin = (mono_current_par.geometry->hbstop-mono_current_par.geometry->hsstrt)*8;
-	var->right_margin = (mono_current_par.geometry->hsstrt-mono_current_par.geometry->hbstrt)*8;
-	var->upper_margin = (mono_current_par.geometry->vbstop-mono_current_par.geometry->vsstrt)*8;
-	var->lower_margin = (mono_current_par.geometry->vsstrt-mono_current_par.geometry->vbstrt)*8;
-	var->hsync_len = (mono_current_par.geometry->hsstop-mono_current_par.geometry->hsstrt)*8;
-	var->vsync_len = (mono_current_par.geometry->vsstop-mono_current_par.geometry->vsstrt)*8;
-	var->sync = 0;
-	if (mono_current_par.geometry->bplcon0 & BPC0_LACE)
-		var->vmode = FB_VMODE_INTERLACED;
-	else if ((boot_info.bi_amiga.chipset == CS_AGA) && (mono_current_par.geometry->fmode & FMODE_BSCAN2))
-		var->vmode = FB_VMODE_DOUBLE;
-	else
-		var->vmode = FB_VMODE_NONINTERLACED;
-
-	for (i = 0; i < arraysize(var->reserved); i++)
-		var->reserved[i] = 0;
-
-	return(0);
+	return ami_encode_fix(fix, &par);
 }
 
+	/*
+	 * Get the User Defined Part of the Display
+	 */
 
-static void mono_amiga_fb_set_disp(int con)
+static int amiga_fb_get_var(struct fb_var_screeninfo *var, int con)
 {
-	struct fb_fix_screeninfo fix;
+	int err = 0;
 
-	mono_amiga_fb_get_fix(&fix, con);
-	if (con == -1)
-		con = 0;
-	disp[con].screen_base = (u_char *)fix.smem_start;
-	disp[con].visual = fix.visual;
-	disp[con].type = fix.type;
-	disp[con].type_aux = fix.type_aux;
-	disp[con].ypanstep = fix.ypanstep;
-	disp[con].ywrapstep = fix.ywrapstep;
-	disp[con].line_length = fix.line_length;
-	disp[con].can_soft_blank = 1;
-	disp[con].inverse = mono_amifb_inverse;
-}
+	if (con == -1) {
+		struct amiga_fb_par par;
 
+		ami_get_par(&par);
+		err = ami_encode_var(var, &par);
+	} else
+		*var = disp[con].var;
+	return err;
+}
 
-static int mono_amiga_fb_set_var(struct fb_var_screeninfo *var, int con)
-{
 	/*
-	 *		Not yet implemented
+	 * Set the User Defined Part of the Display
 	 */
-	return 0;				/* The X server needs this */
-	return(-EINVAL);
-}
-
-
-static short mono_red_normal[] = {
-	((BG_COLOR & 0xff0000)>>8) | ((BG_COLOR & 0xff0000)>>16),
-	((FG_COLOR & 0xff0000)>>8) | ((FG_COLOR & 0xff0000)>>16)
-};
-static short mono_green_normal[] = {
-	((BG_COLOR & 0x00ff00)) | ((BG_COLOR & 0x00ff00)>>8),
-	((FG_COLOR & 0x00ff00)) | ((FG_COLOR & 0x00ff00)>>8)
-};
-static short mono_blue_normal[] = {
-	((BG_COLOR & 0x0000ff)<<8) | ((BG_COLOR & 0x0000ff)),
-	((FG_COLOR & 0x0000ff)<<8) | ((FG_COLOR & 0x0000ff))
-};
 
-static short mono_red_inverse[] = {
-	((BG_COLOR_INV & 0xff0000)>>8) | ((BG_COLOR_INV & 0xff0000)>>16),
-	((FG_COLOR_INV & 0xff0000)>>8) | ((FG_COLOR_INV & 0xff0000)>>16)
-};
-static short mono_green_inverse[] = {
-	((BG_COLOR_INV & 0x00ff00)) | ((BG_COLOR_INV & 0x00ff00)>>8),
-	((FG_COLOR_INV & 0x00ff00)) | ((FG_COLOR_INV & 0x00ff00)>>8)
-};
-static short mono_blue_inverse[] = {
-	((BG_COLOR_INV & 0x0000ff)<<8) | ((BG_COLOR_INV & 0x0000ff)),
-	((FG_COLOR_INV & 0x0000ff)<<8) | ((FG_COLOR & 0x0000ff))
-};
-
-static struct fb_cmap mono_default_cmap_normal = { 0, 2, mono_red_normal, mono_green_normal, mono_blue_normal, NULL };
-static struct fb_cmap mono_default_cmap_inverse = { 0, 2, mono_red_inverse, mono_green_inverse, mono_blue_inverse, NULL };
-
-static int mono_amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+static int amiga_fb_set_var(struct fb_var_screeninfo *var, int con)
 {
-	int i, start;
-	unsigned short *red, *green, *blue, *transp;
-	unsigned int hred, hgreen, hblue, htransp;
-	struct fb_cmap *def_cmap;
+	int err, activate = var->activate;
+	int oldxres, oldyres, oldvxres, oldvyres, oldbpp;
+	struct amiga_fb_par par;
 
-	red = cmap->red;
-	green = cmap->green;
-	blue = cmap->blue;
-	transp = cmap->transp;
-	start = cmap->start;
-	if (start < 0)
-		return(-EINVAL);
 
-	if (mono_amifb_inverse)
-		def_cmap = &mono_default_cmap_inverse;
-	else
-		def_cmap = &mono_default_cmap_normal;
+	/*
+	 * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
+	 * as FB_VMODE_SMOOTH_XPAN is only used internally
+	 */
 
-	for (i = 0; i < cmap->len; i++) {
-		if (i < def_cmap->len) {
-			hred = def_cmap->red[i];
-			hgreen = def_cmap->green[i];
-			hblue = def_cmap->blue[i];
-			if (def_cmap->transp)
-				htransp = def_cmap->transp[i];
-			else
-				htransp = 0;
-		} else
-			hred = hgreen = hblue = htransp = 0;
-		if (kspc) {
-			*red = hred;
-			*green = hgreen;
-			*blue = hblue;
-			if (transp)
-				*transp = htransp;
-		} else {
-			put_fs_word(hred, red);
-			put_fs_word(hgreen, green);
-			put_fs_word(hblue, blue);
-			if (transp)
-				put_fs_word(htransp, transp);
+	if (var->vmode & FB_VMODE_CONUPDATE) {
+		var->vmode |= FB_VMODE_YWRAP;
+		var->xoffset = disp[con].var.xoffset;
+		var->yoffset = disp[con].var.yoffset;
+	}
+	if ((err = ami_decode_var(var, &par)))
+		return err;
+	ami_encode_var(var, &par);
+	if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+		oldxres = disp[con].var.xres;
+		oldyres = disp[con].var.yres;
+		oldvxres = disp[con].var.xres_virtual;
+		oldvyres = disp[con].var.yres_virtual;
+		oldbpp = disp[con].var.bits_per_pixel;
+		disp[con].var = *var;
+		if (oldxres != var->xres || oldyres != var->yres ||
+		    oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
+		    oldbpp != var->bits_per_pixel) {
+			struct fb_fix_screeninfo fix;
+
+			ami_encode_fix(&fix, &par);
+			disp[con].screen_base = (u_char *)fix.smem_start;
+			disp[con].visual = fix.visual;
+			disp[con].type = fix.type;
+			disp[con].type_aux = fix.type_aux;
+			disp[con].ypanstep = fix.ypanstep;
+			disp[con].ywrapstep = fix.ywrapstep;
+			disp[con].line_length = fix.line_length;
+			disp[con].can_soft_blank = 1;
+			disp[con].inverse = amifb_inverse;
+			if (fb_info.changevar)
+				(*fb_info.changevar)(con);
 		}
-		red++;
-		green++;
-		blue++;
-		if (transp)
-			transp++;
+		if (oldbpp != var->bits_per_pixel) {
+			if ((err = alloc_cmap(&disp[con].cmap, 0, 0)))
+				return err;
+			do_install_cmap(con);
+		}
+		if (con == currcon)
+			ami_set_var(&disp[con].var);
 	}
-	return(0);
+	return 0;
 }
 
-
-static int mono_amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
-{
-	/*
-	 *		Not yet implemented
-	 */
-	return(-EINVAL);
-}
-
-
-static int mono_amiga_fb_pan_display(struct fb_var_screeninfo *var, int con)
-{
 	/*
-	 *		Not yet implemented
+	 * Pan or Wrap the Display
+	 *
+	 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
 	 */
-	return(-EINVAL);
-}
-
-
-static int mono_amiga_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
-								  unsigned long arg, int con)
-{
-	return(-EINVAL);
-}
-
-static struct fb_ops mono_amiga_fb_ops = {
-	mono_amiga_fb_get_fix, mono_amiga_fb_get_var, mono_amiga_fb_set_var, mono_amiga_fb_get_cmap,
-	mono_amiga_fb_set_cmap, mono_amiga_fb_pan_display, mono_amiga_fb_ioctl	
-};
-
 
-static int mono_amifb_switch (int con)
-{
-	mono_current_par.y_wrap = disp[con].var.yoffset;
-	mono_current_par.cursor_latch = 1;
-	mono_current_par.scroll_latch = 1;
-	return(0);
-}
-
-
-static int mono_amifb_updatevar(int con)
+static int amiga_fb_pan_display(struct fb_var_screeninfo *var, int con)
 {
-	mono_current_par.y_wrap = disp[con].var.yoffset;
-	mono_current_par.cursor_latch = 1;
-	mono_current_par.scroll_latch = 1;
-	return(0);
+	if (var->vmode & FB_VMODE_YWRAP) {
+		if (var->yoffset<0 || var->yoffset >= disp[con].var.yres_virtual || var->xoffset)
+			return -EINVAL;
+	} else {
+		/*
+		 * TODO: There will be problems when xpan!=1, so some columns
+		 * on the right side will never be seen
+		 */
+		if (var->xoffset+disp[con].var.xres > upx(16<<maxfmode, disp[con].var.xres_virtual) ||
+		    var->yoffset+disp[con].var.yres > disp[con].var.yres_virtual)
+			return -EINVAL;
+	}
+	if (con == currcon)
+		ami_pan_var(var);
+	disp[con].var.xoffset = var->xoffset;
+	disp[con].var.yoffset = var->yoffset;
+	if (var->vmode & FB_VMODE_YWRAP)
+		disp[con].var.vmode |= FB_VMODE_YWRAP;
+	else
+		disp[con].var.vmode &= ~FB_VMODE_YWRAP;
+	return 0;
 }
 
+	/*
+	 * Get the Colormap
+	 */
 
-static void mono_amifb_blank(int blank)
+static int amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
 {
-	if (blank)
-		mono_do_blank = 1;
+	if (con == currcon) /* current console? */
+		return do_fb_get_cmap(cmap, &disp[con].var, kspc);
+	else if (disp[con].cmap.len) /* non default colormap? */
+		copy_cmap(&disp[con].cmap, cmap, kspc ? 0 : 2);
 	else
-		mono_do_unblank = 1;
+		copy_cmap(get_default_colormap(disp[con].var.bits_per_pixel),
+		          cmap, kspc ? 0 : 2);
+	return 0;
 }
 
+	/*
+	 * Set the Colormap
+	 */
 
-static struct fb_info *mono_amiga_fb_init(long *mem_start)
+static int amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
 {
-	int mode = mono_amifb_mode;
-	ulong model;
-	int inverse_video = mono_amifb_inverse;
 	int err;
 
-	err=register_framebuffer("Amiga Builtin", &node, &mono_amiga_fb_ops,  mono_num_mono_amiga_fb_predefined,
-									 mono_mono_amiga_fb_predefined);
-
-	model = boot_info.bi_un.bi_ami.model;
-	if (mode == -1)
-		if (boot_info.bi_amiga.chipset == CS_AGA)
-			mode = AGA_DEFMODE;
-		else if (model == AMI_3000)
-			mode = boot_info.bi_un.bi_ami.vblank == 50 ? OCS_PAL_3000_DEFMODE : OCS_NTSC_3000_DEFMODE;
-		else
-			mode = boot_info.bi_un.bi_ami.vblank == 50 ? OCS_PAL_LOWEND_DEFMODE : OCS_NTSC_LOWEND_DEFMODE;
-
-	mono_init_vblank();
-	mono_display_init(disp, &mono_modes[mode], inverse_video);
-	if (!add_isr(IRQ_AMIGA_VERTB, mono_amifb_interrupt, 0, NULL, "frame buffer"))
-		panic("Couldn't add vblank interrupt");
-
-	mono_amiga_fb_get_var(&disp[0].var, 0);
-	if (mono_amifb_inverse)
-		disp[0].cmap = mono_default_cmap_inverse;
+	if (!disp[con].cmap.len) {		/* no colormap allocated? */
+		if ((err = alloc_cmap(&disp[con].cmap,
+		                      1<<disp[con].var.bits_per_pixel, 0)))
+			return err;
+	}
+	if (con == currcon)			/* current console? */
+		return do_fb_set_cmap(cmap, &disp[con].var, kspc);
 	else
-		disp[0].cmap = mono_default_cmap_normal;
-	mono_amiga_fb_set_disp(-1);
-
-	strcpy(fb_info.modename, "Amiga Builtin ");
-	fb_info.disp = disp;
-	fb_info.switch_con = &mono_amifb_switch;
-	fb_info.updatevar = &mono_amifb_updatevar;
-	fb_info.blank = &mono_amifb_blank;	
-	strcat(fb_info.modename, mono_modes[mode].modename);
-
-	return(&fb_info);
+		copy_cmap(cmap, &disp[con].cmap, kspc ? 0 : 1);
+	return 0;
 }
-#endif /* USE_MONO_AMIFB_IF_NON_AGA */
-
 
-/* -------------------- OCS specific routines ------------------------------- */
-
-
-#ifdef CONFIG_AMIFB_OCS
-   /*
-    *    Initialization
-    *
-    *    Allocate the required chip memory.
-    *    Set the default video mode for this chipset. If a video mode was
-    *    specified on the command line, it will override the default mode.
-    */
+	/*
+	 * Amiga Frame Buffer Specific ioctls
+	 */
 
-static int ocs_init(void)
+static int amiga_fb_ioctl(struct inode *inode, struct file *file,
+                          u_int cmd, u_long arg, int con)
 {
-   u_long p;
-
-   /*
-    *    Disable Display DMA
-    */
-
-   custom.dmacon = DMAF_ALL | DMAF_MASTER;
-
-   /*
-    *    Set the Default Video Mode
-    */
-
-   if (!amifb_mode)
-      amifb_mode = get_video_mode(boot_info.bi_un.bi_ami.vblank == 50 ?
-                                  DEFMODE_PAL : DEFMODE_NTSC);
-
-   /*
-    *    Allocate Chip RAM Structures
-    */
-
-   videomemorysize = VIDEOMEMSIZE_OCS;
+	int i;
 
+	switch (cmd) {
+		case FBIOGET_FCURSORINFO : {
+			struct fb_fix_cursorinfo crsrfix;
+			
+			i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrfix));
+			if (!i) {
+				i = amiga_fb_get_fix_cursorinfo(&crsrfix, con);
+				memcpy_tofs((void *)arg, &crsrfix, sizeof(crsrfix));
+			}
+			return i;
+		}
+		case FBIOGET_VCURSORINFO : {
+			struct fb_var_cursorinfo crsrvar;
 
-   ...
-   ...
-   ...
+			i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrvar));
+			if (!i) {
+				i = amiga_fb_get_var_cursorinfo(&crsrvar,
+					((struct fb_var_cursorinfo *)arg)->data, con);
+				memcpy_tofs((void *)arg, &crsrvar, sizeof(crsrvar));
+			}
+			return i;
+		}
+		case FBIOPUT_VCURSORINFO : {
+			struct fb_var_cursorinfo crsrvar;
 
+			i = verify_area(VERIFY_READ, (void *)arg, sizeof(crsrvar));
+			if (!i) {
+				memcpy_fromfs(&crsrvar, (void *)arg, sizeof(crsrvar));
+				i = amiga_fb_set_var_cursorinfo(&crsrvar,
+					((struct fb_var_cursorinfo *)arg)->data, con);
+			}
+			return i;
+		}
+		case FBIOGET_CURSORSTATE : {
+			struct fb_cursorstate crsrstate;
 
-   /*
-    *    Enable Display DMA
-    */
+			i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrstate));
+			if (!i) {
+				i = amiga_fb_get_cursorstate(&crsrstate, con);
+				memcpy_tofs((void *)arg, &crsrstate, sizeof(crsrstate));
+			}
+			return i;
+		}
+		case FBIOPUT_CURSORSTATE : {
+			struct fb_cursorstate crsrstate;
 
-   custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
-                   DMAF_SPRITE;
+			i = verify_area(VERIFY_READ, (void *)arg, sizeof(crsrstate));
+			if (!i) {
+				memcpy_fromfs(&crsrstate, (void *)arg, sizeof(crsrstate));
+				i = amiga_fb_set_cursorstate(&crsrstate, con);
+			}
+			return i;
+		}
+#ifdef DEBUG
+		case FBCMD_GET_CURRENTPAR : {
+			struct amiga_fb_par par;
+
+			i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct amiga_fb_par));
+			if (!i) {
+				ami_get_par(&par);
+				memcpy_tofs((void *)arg, &par, sizeof(struct amiga_fb_par));
+			}
+			return i;
+		}
+		case FBCMD_SET_CURRENTPAR : {
+			struct amiga_fb_par par;
 
-   return(0);
+			i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct amiga_fb_par));
+			if (!i) {
+				memcpy_fromfs(&par, (void *)arg, sizeof(struct amiga_fb_par));
+				ami_set_par(&par);
+			}
+			return i;
+		}
+#endif */ DEBUG */
+	}
+	return -EINVAL;
 }
-#endif /* CONFIG_AMIFB_OCS */
 
+	/*
+	 * Hardware Cursor
+	 */
 
-/* -------------------- ECS specific routines ------------------------------- */
-
-
-#ifdef CONFIG_AMIFB_ECS
-   /*
-    *    Initialization
-    *
-    *    Allocate the required chip memory.
-    *    Set the default video mode for this chipset. If a video mode was
-    *    specified on the command line, it will override the default mode.
-    */
-
-static int ecs_init(void)
+static int amiga_fb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con)
 {
-   u_long p;
-
-   /*
-    *    Disable Display DMA
-    */
-
-   custom.dmacon = DMAF_ALL | DMAF_MASTER;
-
-   /*
-    *    Set the Default Video Mode
-    */
-
-   if (!amifb_mode)
-      if (AMIGAHW_PRESENT(AMBER_FF))
-         amifb_mode = get_video_mode(boot_info.bi_un.bi_ami.vblank == 50 ?
-                                     DEFMODE_AMBER_PAL : DEFMODE_AMBER_NTSC);
-      else
-         amifb_mode = get_video_mode(boot_info.bi_un.bi_ami.vblank == 50 ?
-                                     DEFMODE_PAL : DEFMODE_NTSC);
-
-   /*
-    *    Allocate Chip RAM Structures
-    */
-
-   if (boot_info.bi_amiga.chip_size > 1048576)
-      videomemorysize = VIDEOMEMSIZE_ECS_2M;
-   else
-      videomemorysize = VIDEOMEMSIZE_ECS_1M;
-
-
-   ...
-   ...
-   ...
-
-
-   /*
-    *    Enable Display DMA
-    */
-
-   custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
-                   DMAF_SPRITE;
-
-   return(0);
-}
-#endif /* CONFIG_AMIFB_ECS */
-
-
-/* -------------------- AGA specific routines ------------------------------- */
-
-
-#ifdef CONFIG_AMIFB_AGA
-   /*
-    *    Macros for the conversion from real world values to hardware register
-    *    values (and vice versa).
-    *
-    *    This helps us to keep our attention on the real stuff...
-    *
-    *    Hardware limits:
-    *
-    *       parameter     min      max     step
-    *       ---------     ---     ----     ----
-    *       diwstrt_h       0     2047        1
-    *       diwstrt_v       0     2047        1
-    *       diwstop_h       0     2047        1
-    *       diwstop_v       0     2047        1
-    *
-    *       ddfstrt         0     2032       16
-    *       ddfstop         0     2032       16
-    *
-    *       htotal          8     2048        8
-    *       hsstrt          0     2040        8
-    *       hsstop          0     2040        8
-    *       vtotal          1     2048        1
-    *       vsstrt          0     2047        1
-    *       vsstop          0     2047        1
-    *       hcenter         0     2040        8
-    *
-    *       hbstrt          0     2047        1
-    *       hbstop          0     2047        1
-    *       vbstrt          0     2047        1
-    *       vbstop          0     2047        1
-    *
-    *    Horizontal values are in 35 ns (SHRES) pixels
-    *    Vertical values are in scanlines
-    */
-
-/* bplcon1 (smooth scrolling) */
-
-#define hscroll2hw(hscroll) \
-   (((hscroll)<<12 & 0x3000) | ((hscroll)<<8 & 0xc300) | \
-    ((hscroll)<<4 & 0x0c00) | ((hscroll)<<2 & 0x00f0) | ((hscroll)>>2 & 0x000f))
-
-#define hw2hscroll(hscroll) \
-   (((hscroll)>>8 & 0x00c3) | ((hscroll)<<2 & 0x003c))
-
-/* diwstrt/diwstop/diwhigh (visible display window) */
-
-#define diwstrt2hw(diwstrt_h, diwstrt_v) \
-   (((diwstrt_v)<<8 & 0xff00) | ((diwstrt_h)>>2 & 0x00ff))
-#define diwstop2hw(distop_h, diwstop_v) \
-   (((diwstop_v)<<8 & 0xff00) | ((diwstop_h)>>2 & 0x00ff))
-#define diw2hw_high(diwstrt_h, diwstrt_v, distop_h, diwstop_v) \
-   (((diwstop_h)<<3 & 0x2000) | ((diwstop_h)<<11 & 0x1800) | \
-    ((diwstop_v) & 0x0700) | ((diwstrt_h)>>5 & 0x0020) | \
-    ((diwstrt_h)<<3 & 0x0018) | ((diwstrt_v)>>8 & 0x0007))
-
-#define hw2diwstrt_h(diwstrt, diwhigh) \
-   (((diwhigh)<<5 & 0x0400) | ((diwstrt)<<2 & 0x03fc) | ((diwhigh)>>3 & 0x0003))
-#define hw2diwstrt_v(diwstrt, diwhigh) \
-   (((diwhigh)<<8 & 0x0700) | ((diwstrt)>>8 & 0x00ff))
-#define hw2diwstop_h(diwstop, diwhigh) \
-   (((diwhigh)>>3 & 0x0400) | ((diwstop)<<2 & 0x03fc) | \
-    ((diwhigh)>>11 & 0x0003))
-#define hw2diwstop_v(diwstop, diwhigh) \
-   (((diwhigh) & 0x0700) | ((diwstop)>>8 & 0x00ff))
-
-/* ddfstrt/ddfstop (display DMA) */
-
-#define ddfstrt2hw(ddfstrt)   (div8(ddfstrt) & 0x00fe)
-#define ddfstop2hw(ddfstop)   (div8(ddfstop) & 0x00fe)
-
-#define hw2ddfstrt(ddfstrt)   ((ddfstrt)<<3)
-#define hw2ddfstop(ddfstop)   ((ddfstop)<<3)
-
-/* hsstrt/hsstop/htotal/vsstrt/vsstop/vtotal (sync timings) */
-
-#define hsstrt2hw(hsstrt)     (div8(hsstrt))
-#define hsstop2hw(hsstop)     (div8(hsstop))
-#define htotal2hw(htotal)     (div8(htotal)-1)
-#define vsstrt2hw(vsstrt)     (vsstrt)
-#define vsstop2hw(vsstop)     (vsstop)
-#define vtotal2hw(vtotal)     ((vtotal)-1)
-
-#define hw2hsstrt(hsstrt)     ((hsstrt)<<3)
-#define hw2hsstop(hsstop)     ((hsstop)<<3)
-#define hw2htotal(htotal)     (((htotal)+1)<<3)
-#define hw2vsstrt(vsstrt)     (vsstrt)
-#define hw2vsstop(vsstop)     (vsstop)
-#define hw2vtotal(vtotal)     ((vtotal)+1)
-
-/* hbstrt/hbstop/vbstrt/vbstop (blanking timings) */
-
-#define hbstrt2hw(hbstrt)     (((hbstrt)<<8 & 0x0700) | ((hbstrt)>>3 & 0x00ff))
-#define hbstop2hw(hbstop)     (((hbstop)<<8 & 0x0700) | ((hbstop)>>3 & 0x00ff))
-#define vbstrt2hw(vbstrt)     (vbstrt)
-#define vbstop2hw(vbstop)     (vbstop)
-
-#define hw2hbstrt(hbstrt)     (((hbstrt)<<3 & 0x07f8) | ((hbstrt)>>8 & 0x0007))
-#define hw2hbstop(hbstop)     (((hbstop)<<3 & 0x07f8) | ((hbstop)>>8 & 0x0007))
-#define hw2vbstrt(vbstrt)     (vbstrt)
-#define hw2vbstop(vbstop)     (vbstop)
-
-/* color */
-
-#define rgb2hw_high(red, green, blue) \
-   (((red)<<4 & 0xf00) | ((green) & 0x0f0) | ((blue)>>4 & 0x00f))
-#define rgb2hw_low(red, green, blue) \
-   (((red)<<8 & 0xf00) | ((green)<<4 & 0x0f0) | ((blue) & 0x00f))
-
-#define hw2red(high, low)     (((high)>>4 & 0xf0) | ((low)>>8 & 0x0f))
-#define hw2green(high, low)   (((high) & 0xf0) | ((low)>>4 & 0x0f))
-#define hw2blue(high, low)    (((high)<<4 & 0xf0) | ((low) & 0x0f))
-
-/* sprpos/sprctl (sprite positioning) */
-
-#define spr2hw_pos(start_v, start_h) \
-   (((start_v)<<8&0xff00) | ((start_h)>>3&0x00ff))
-#define spr2hw_ctl(start_v, start_h, stop_v) \
-   (((stop_v)<<8&0xff00) | ((start_v)>>3&0x0040) | ((stop_v)>>4&0x0020) | \
-    ((start_h)<<3&0x0018) | ((start_v)>>6&0x0004) | ((stop_v)>>7&0x0002) | \
-    ((start_h)>>2&0x0001))
-
-
-   /*
-    *    Hardware Cursor
-    */
-
-struct aga_cursorsprite {
-   u_short sprpos;
-   u_short pad1[3];
-   u_short sprctl;
-   u_short pad2[3];
-   union {
-      struct {
-         u_long data[64*4];
-         u_long trailer[4];
-      } nonlaced;
-      struct {
-         u_long data[32*4];
-         u_long trailer[4];
-      } laced;
-   } u;
-};
-
-struct aga_dummysprite {
-   u_short sprpos;
-   u_short pad1[3];
-   u_short sprctl;
-   u_short pad2[3];
-   u_long data[4];
-   u_long trailer[4];
-};
-
+	return ami_get_fix_cursorinfo(fix, con);
+}
 
-   /*
-    *    Pixel modes for Bitplanes and Sprites
-    */
+static int amiga_fb_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con)
+{
+	return ami_get_var_cursorinfo(var, data, con);
+}
 
-static u_short bplpixmode[3] = {
-   BPC0_SHRES,                   /*  35 ns / 28 MHz */
-   BPC0_HIRES,                   /*  70 ns / 14 MHz */
-   0                             /* 140 ns /  7 MHz */
-};
+static int amiga_fb_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con)
+{
+	return ami_set_var_cursorinfo(var, data, con);
+}
 
-static u_short sprpixmode[3] = {
-   BPC3_SPRES1 | BPC3_SPRES0,    /*  35 ns / 28 MHz */
-   BPC3_SPRES1,                  /*  70 ns / 14 MHz */
-   BPC3_SPRES0                   /* 140 ns /  7 MHz */
-};
+static int amiga_fb_get_cursorstate(struct fb_cursorstate *state, int con)
+{
+	return ami_get_cursorstate(state, con);
+}
 
+static int amiga_fb_set_cursorstate(struct fb_cursorstate *state, int con)
+{
+	return ami_set_cursorstate(state, con);
+}
 
-   /*
-    *    Initialization
-    *
-    *    Allocate the required chip memory.
-    *    Set the default video mode for this chipset. If a video mode was
-    *    specified on the command line, it will override the default mode.
-    */
+	/*
+	 * Initialisation
+	 */
 
-static int aga_init(void)
+struct fb_info *amiga_fb_init(long *mem_start)
 {
-   u_long p;
+	int err, tag, i;
+	u_long chipptr;
 
-   /*
-    *    Disable Display DMA
-    */
+	/*
+	 * Check for a Graphics Board
+	 */
 
-   custom.dmacon = DMAF_ALL | DMAF_MASTER;
+#ifdef CONFIG_FB_CYBER
+	if (amifb_Cyber)
+		return Cyber_fb_init(mem_start);
+#endif /* CONFIG_FB_CYBER */
+#ifdef CONFIG_GSP_RESOLVER
+	if (amifb_resolver){
+		custom.dmacon = DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
+				DMAF_BLITTER | DMAF_SPRITE;
+		return NULL;
+	}
+#endif
 
-   /*
-    *    Set the Default Video Mode
-    */
+	/*
+	 * Use the Builtin Chipset
+	 */
 
-   if (!amifb_mode)
-      amifb_mode = get_video_mode(DEFMODE_AGA);
+	if (!AMIGAHW_PRESENT(AMI_VIDEO))
+		return NULL;
 
-   /*
-    *    Allocate Chip RAM Structures
-    */
+	custom.dmacon = DMAF_ALL | DMAF_MASTER;
 
-   if (boot_info.bi_amiga.chip_size > 1048576)
-      videomemorysize = VIDEOMEMSIZE_AGA_2M;
-   else
-      videomemorysize = VIDEOMEMSIZE_AGA_1M;
+	switch (boot_info.bi_amiga.chipset) {
+#ifdef CONFIG_AMIFB_OCS
+		case CS_OCS:
+			strcat(amiga_fb_name, "OCS");
+default_chipset:
+			chipset = TAG_OCS;
+			maxdepth[TAG_SHRES] = 0;	/* OCS means no SHRES */
+			maxdepth[TAG_HIRES] = 4;
+			maxdepth[TAG_LORES] = 6;
+			maxfmode = TAG_FMODE_1;
+			if (!amifb_usermode)		/* Set the Default Video Mode */
+				get_video_mode(boot_info.bi_un.bi_ami.vblank == 50 ?
+				               DEFMODE_PAL : DEFMODE_NTSC);
+			videomemorysize = VIDEOMEMSIZE_OCS;
+			break;
+#endif /* CONFIG_AMIFB_OCS */
 
-   p = chipalloc(videomemorysize+                     /* Bitplanes */
-                 sizeof(struct clist_hdr)+            /* Copper Lists */
-                 2*sizeof(struct clist_dyn)+
-                 2*sizeof(struct aga_cursorsprite)+   /* Sprites */
-                 sizeof(struct aga_dummysprite));
+#ifdef CONFIG_AMIFB_ECS
+		case CS_ECS:
+			strcat(amiga_fb_name, "ECS");
+			chipset = TAG_ECS;
+			maxdepth[TAG_SHRES] = 2;
+			maxdepth[TAG_HIRES] = 4;
+			maxdepth[TAG_LORES] = 6;
+			maxfmode = TAG_FMODE_1;
+			if (!amifb_usermode) {		/* Set the Default Video Mode */
+				if (AMIGAHW_PRESENT(AMBER_FF))
+					get_video_mode(boot_info.bi_un.bi_ami.vblank == 50 ?
+					               DEFMODE_AMBER_PAL : DEFMODE_AMBER_NTSC);
+				else
+					get_video_mode(boot_info.bi_un.bi_ami.vblank == 50 ?
+					               DEFMODE_PAL : DEFMODE_NTSC);
+			}
+			if (boot_info.bi_amiga.chip_size > 1048576)
+				videomemorysize = VIDEOMEMSIZE_ECS_2M;
+			else
+				videomemorysize = VIDEOMEMSIZE_ECS_1M;
+			break;
+#endif /* CONFIG_AMIFB_ECS */
 
-   assignchunk(videomemory, u_long, p, videomemorysize);
-   assignchunk(clist_hdr, struct clist_hdr *, p, sizeof(struct clist_hdr));
-   assignchunk(clist_lof, struct clist_dyn *, p, sizeof(struct clist_dyn));
-   assignchunk(clist_shf, struct clist_dyn *, p, sizeof(struct clist_dyn));
-   assignchunk(lofsprite, u_long *, p, sizeof(struct aga_cursorsprite));
-   assignchunk(shfsprite, u_long *, p, sizeof(struct aga_cursorsprite));
-   assignchunk(dummysprite, u_long *, p, sizeof(struct aga_dummysprite));
+#ifdef CONFIG_AMIFB_AGA
+		case CS_AGA:
+			strcat(amiga_fb_name, "AGA");
+			chipset = TAG_AGA;
+			maxdepth[TAG_SHRES] = 8;
+			maxdepth[TAG_HIRES] = 8;
+			maxdepth[TAG_LORES] = 8;
+			maxfmode = TAG_FMODE_4;
+			if (!amifb_usermode)		/* Set the Default Video Mode */
+				get_video_mode(DEFMODE_AGA);
+			if (boot_info.bi_amiga.chip_size > 1048576)
+				videomemorysize = VIDEOMEMSIZE_AGA_2M;
+			else
+				videomemorysize = VIDEOMEMSIZE_AGA_1M;
+			break;
+#endif /* CONFIG_AMIFB_AGA */
 
-   /*
-    *    Make sure the Copper has something to do
-    */
+		default:
+#ifdef CONFIG_AMIFB_OCS
+			printk("Unknown graphics chipset, defaulting to OCS\n");
+			strcat(amiga_fb_name, "Unknown");
+			goto default_chipset;
+#else /* CONFIG_AMIFB_OCS */
+			panic("Unknown graphics chipset, no default driver");
+#endif /* CONFIG_AMIFB_OCS */
+			break;
+	}
 
-   aga_build_clist_hdr(clist_hdr);
+	/*
+	 * Calculate the Pixel Clock Values for this Machine
+	 */
 
-   custom.cop1lc = (u_short *)ZTWO_PADDR(clist_hdr);
-   custom.cop2lc = (u_short *)ZTWO_PADDR(&clist_hdr->wait_forever);
-   custom.copjmp1 = 0;
+	pixclock[TAG_SHRES] = DIVUL(25E9, amiga_eclock);	/* SHRES:  35 ns / 28 MHz */
+	pixclock[TAG_HIRES] = DIVUL(50E9, amiga_eclock);	/* HIRES:  70 ns / 14 MHz */
+	pixclock[TAG_LORES] = DIVUL(100E9, amiga_eclock); 	/* LORES: 140 ns /  7 MHz */
 
-   /*
-    *    Enable Display DMA
-    */
+	/*
+	 * Replace the Tag Values with the Real Pixel Clock Values
+	 */
 
-   custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
-                   DMAF_SPRITE;
+	for (i = 0; i < NUM_PREDEF_MODES; i++) {
+		tag = amiga_fb_predefined[i].pixclock;
+		if (tag == TAG_SHRES || tag == TAG_HIRES || tag == TAG_LORES) {
+			amiga_fb_predefined[i].pixclock = pixclock[tag];
+			if (amiga_fb_predefined[i].bits_per_pixel > maxdepth[tag])
+				amiga_fb_predefined[i].bits_per_pixel = maxdepth[tag];
+		}
+	}
 
-   /*
-    *    These hardware register values will never be changed later
-    */
+	err = register_framebuffer(amiga_fb_name, &node, &amiga_fb_ops,
+	                           NUM_TOTAL_MODES, amiga_fb_predefined);
+	if (err < 0)
+		panic("Cannot register frame buffer");
+
+	chipptr = chipalloc(videomemorysize+
+	                    SPRITEMEMSIZE+
+	                    DUMMYSPRITEMEMSIZE+
+	                    COPINITSIZE+
+	                    4*COPLISTSIZE);
+
+	assignchunk(videomemory, u_long, chipptr, videomemorysize);
+	assignchunk(spritememory, u_long, chipptr, SPRITEMEMSIZE);
+	assignchunk(dummysprite, u_short *, chipptr, DUMMYSPRITEMEMSIZE);
+	assignchunk(copdisplay.init, copins *, chipptr, COPINITSIZE);
+	assignchunk(copdisplay.list[0][0], copins *, chipptr, COPLISTSIZE);
+	assignchunk(copdisplay.list[0][1], copins *, chipptr, COPLISTSIZE);
+	assignchunk(copdisplay.list[1][0], copins *, chipptr, COPLISTSIZE);
+	assignchunk(copdisplay.list[1][1], copins *, chipptr, COPLISTSIZE);
 
-   custom.bplcon2 = BPC2_KILLEHB | BPC2_PF2P2 | BPC2_PF1P2;
-   custom.bplcon4 = BPC4_ESPRM4 | BPC4_OSPRM4;
+	memset(dummysprite, 0, DUMMYSPRITEMEMSIZE);
 
-   return(0);
-}
+	/*
+	 * Enable Display DMA
+	 */
 
+	custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
+	                DMAF_BLITTER | DMAF_SPRITE;
 
-   /*
-    *    This function should fill in the `fix' structure based on the
-    *    values in the `par' structure.
-    */
+	/*
+	 * Make sure the Copper has something to do
+	 */
 
-static int aga_encode_fix(struct fb_fix_screeninfo *fix,
-                          struct amiga_fb_par *par)
-{
-   int i;
+	ami_init_copper();
 
-   strcpy(fix->id, amiga_fb_name);
-   fix->smem_start = videomemory;
-   fix->smem_len = videomemorysize;
-
-   if (amifb_ilbm) {
-      fix->type = FB_TYPE_INTERLEAVED_PLANES;
-      fix->type_aux = par->next_line;
-   } else {
-      fix->type = FB_TYPE_PLANES;
-      fix->type_aux = 0;
-   }
-   fix->visual = FB_VISUAL_PSEUDOCOLOR;
-
-   if (par->diwstrt_h >= 323)
-      fix->xpanstep = 1;
-   else
-      fix->xpanstep = 64;
-   fix->ypanstep = 1;
-
-   if (hw2ddfstrt(par->ddfstrt) >= (par->bpp-1)*64)
-      fix->ywrapstep = 1;
-   else
-      fix->ywrapstep = 0;
-
-   fix->line_length = 0;
-   for (i = 0; i < arraysize(fix->reserved); i++)
-      fix->reserved[i] = 0;
-
-   return(0);
-}
+	check_default_mode();
 
+	if (request_irq(IRQ3, amifb_interrupt, IRQ_FLG_LOCK,
+	                "fb vertb handler", NULL))
+		panic("Couldn't add vblank interrupt\n");
+	ami_intena_vals[IRQ_IDX(IRQ_AMIGA_VERTB)] = IF_COPER;
+	ami_intena_vals[IRQ_IDX(IRQ_AMIGA_COPPER)] = 0;
+	custom.intena = IF_VERTB;
+	custom.intena = IF_SETCLR | IF_COPER;
 
-   /*
-    *    Get the video params out of `var'. If a value doesn't fit, round
-    *    it up, if it's too big, return -EINVAL.
-    */
+	strcpy(fb_info.modename, amiga_fb_name);
+	fb_info.changevar = NULL;
+	fb_info.disp = disp;
+	fb_info.switch_con = &amifbcon_switch;
+	fb_info.updatevar = &amifbcon_updatevar;
+	fb_info.blank = &amifbcon_blank;
 
-static int aga_decode_var(struct fb_var_screeninfo *var,
-                          struct amiga_fb_par *par)
-{
-   u_short clk_shift, line_shift_incd;
-   u_long upper, lower, hslen, vslen;
-   int xres_n, yres_n, xoffset_n;                              /* normalized */
-   u_long left_n, right_n, upper_n, lower_n, hslen_n, vslen_n; /* normalized */
-   u_long diwstrt_h, diwstrt_v, diwstop_h, diwstop_v;
-   u_long hsstrt, vsstrt, hsstop, vsstop, htotal, vtotal;
-   u_long ddfmin, ddfmax, ddfstrt, ddfstop, hscroll;
-   u_long hrate, vrate;
-   u_short loopcnt = 0;
-
-   /*
-    *    Find a matching Pixel Clock
-    */
-
-   for (clk_shift = 0; clk_shift < 3; clk_shift++)
-      if (var->pixclock <= pixclock[clk_shift])
-         break;
-   if (clk_shift >= 3)
-      return(-EINVAL);
-   par->clk_shift = clk_shift;
-
-   /*
-    *    Round up the Geometry Values (if necessary)
-    */
-
-   par->xres = max(var->xres, 64);
-   par->yres = max(var->yres, 64);
-   par->vxres = up64(max(var->xres_virtual, par->xres));
-   par->vyres = max(var->yres_virtual, par->yres);
-
-   par->bpp = var->bits_per_pixel;
-   if (par->bpp > 8)
-      return(-EINVAL);
-
-   if (!var->nonstd) {
-      if (par->bpp < 1)
-         par->bpp = 1;
-   } else if (var->nonstd == FB_NONSTD_HAM)
-      par->bpp = par->bpp <= 6 ? 6 : 8;
-   else
-      return(-EINVAL);
-
-   upper = var->upper_margin;
-   lower = var->lower_margin;
-   hslen = var->hsync_len;
-   vslen = var->vsync_len;
-
-   par->vmode = var->vmode;
-   switch (par->vmode & FB_VMODE_MASK) {
-      case FB_VMODE_NONINTERLACED:
-         line_shift_incd = 1;
-         break;
-      case FB_VMODE_INTERLACED:
-         line_shift_incd = 0;
-         if (par->yres & 1)
-            par->yres++;               /* even */
-         if (upper & 1)
-            upper++;                   /* even */
-         if (!(lower & 1))
-            lower++;                   /* odd */
-         if (vslen & 1)
-            vslen++;                   /* even */
-         break;
-      case FB_VMODE_DOUBLE:
-         line_shift_incd = 2;
-         break;
-      default:
-         return(-EINVAL);
-         break;
-   }
-
-   par->xoffset = var->xoffset;
-   par->yoffset = var->yoffset;
-   if (par->vmode & FB_VMODE_YWRAP) {
-      if (par->xoffset || par->yoffset < 0 || par->yoffset >= par->yres)
-         return(-EINVAL);
-   } else {
-      if (par->xoffset < 0 || par->xoffset+par->xres > par->vxres ||
-          par->yoffset < 0 || par->yoffset+par->yres > par->vyres)
-         return(-EINVAL);
-   }
-
-   if (var->sync & FB_SYNC_BROADCAST) {
-      if (hslen || vslen)
-         return(-EINVAL);
-   } else {
-      hslen = hslen < 1 ? 1 : hslen;
-      vslen = vslen < 1 ? 1 : vslen;
-   }
-
-   /*
-    *    Check the Memory Requirements
-    */
-
-   if (par->vxres*par->vyres*par->bpp > videomemorysize<<3)
-      return(-EINVAL);
-
-   /*
-    *    Normalize all values:
-    *
-    *      - horizontal: in 35 ns (SHRES) pixels
-    *      - vertical: in non-interlaced scanlines
-    */
-
-   xres_n = par->xres<<clk_shift;
-   xoffset_n = par->xoffset<<clk_shift;
-   yres_n = par->yres<<line_shift_incd>>1;
-
-   left_n = var->left_margin<<clk_shift;
-   right_n = var->right_margin<<clk_shift;
-   hslen_n = hslen<<clk_shift;
-   upper_n = upper<<line_shift_incd>>1;
-   lower_n = lower<<line_shift_incd>>1;
-   vslen_n = vslen<<line_shift_incd>>1;
-
-   /*
-    *    Vertical and Horizontal Timings
-    */
-
-   par->bplcon3 = sprpixmode[clk_shift];
-aga_calculate_timings:
-   if (var->sync & FB_SYNC_BROADCAST) {
-      if (upper_n+yres_n+lower_n == PAL_WINDOW_V) {
-         /* PAL video mode */
-         diwstrt_v = PAL_DIWSTRT_V+upper_n;
-         diwstop_v = diwstrt_v+yres_n;
-         diwstrt_h = PAL_DIWSTRT_H+left_n;
-         diwstop_h = diwstrt_h+xres_n+1;
-         par->htotal = htotal2hw(PAL_HTOTAL);
-         hrate = 15625;
-         vrate = 50;
-         par->beamcon0 = BMC0_PAL;
-      } else if (upper_n+yres_n+lower_n == NTSC_WINDOW_V) {
-         /* NTSC video mode */
-         diwstrt_v = NTSC_DIWSTRT_V+upper_n;
-         diwstop_v = diwstrt_v+yres_n;
-         diwstrt_h = NTSC_DIWSTRT_H+left_n;
-         diwstop_h = diwstrt_h+xres_n+1;
-         par->htotal = htotal2hw(NTSC_HTOTAL);
-         hrate = 15750;
-         vrate = 60;
-         par->beamcon0 = 0;
-      } else
-         return(-EINVAL);
-   } else {
-      /* Programmable video mode */
-      vsstrt = lower_n;
-      vsstop = vsstrt+vslen_n;
-      diwstrt_v = vsstop+upper_n;
-      diwstop_v = diwstrt_v+yres_n;
-      vtotal = diwstop_v;
-      hslen_n = up8(hslen_n);
-      htotal = up8(left_n+xres_n+right_n+hslen_n);
-      if (vtotal > 2048 || htotal > 2048)
-         return(-EINVAL);
-      right_n = htotal-left_n-xres_n-hslen_n;
-      hsstrt = down8(right_n+4);
-      hsstop = hsstrt+hslen_n;
-      diwstop_h = htotal+hsstrt-right_n+1;
-      diwstrt_h = diwstop_h-xres_n-1;
-      hrate = (amiga_masterclock+htotal/2)/htotal;
-      vrate = (amiga_masterclock+htotal*vtotal/2)/(htotal*vtotal);
-      par->bplcon3 |= BPC3_BRDRBLNK | BPC3_EXTBLKEN;
-      par->beamcon0 = BMC0_HARDDIS | BMC0_VARVBEN | BMC0_LOLDIS |
-                      BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARBEAMEN |
-                      BMC0_PAL | BMC0_VARCSYEN;
-      if (var->sync & FB_SYNC_HOR_HIGH_ACT)
-         par->beamcon0 |= BMC0_HSYTRUE;
-      if (var->sync & FB_SYNC_VERT_HIGH_ACT)
-         par->beamcon0 |= BMC0_VSYTRUE;
-      if (var->sync & FB_SYNC_COMP_HIGH_ACT)
-         par->beamcon0 |= BMC0_CSYTRUE;
-      par->htotal = htotal2hw(htotal);
-      par->hsstrt = hsstrt2hw(hsstrt);
-      par->hsstop = hsstop2hw(hsstop);
-      par->vtotal = vtotal2hw(vtotal);
-      par->vsstrt = vsstrt2hw(vsstrt);
-      par->vsstop = vsstop2hw(vsstop);
-      par->hcenter = par->hsstrt+(par->htotal>>1);
-   }
-   par->diwstrt_v = diwstrt_v;
-   par->diwstrt_h = diwstrt_h;
-   par->crsr_x = 0;
-   par->crsr_y = 0;
-
-   /*
-    *    DMA Timings
-    */
-
-   ddfmin = down64(xoffset_n);
-   ddfmax = up64(xoffset_n+xres_n);
-   hscroll = diwstrt_h-68-mod64(xoffset_n);
-   ddfstrt = down64(hscroll);
-   if (ddfstrt < 128) {
-      right_n += (128-hscroll);
-      /* Prevent an infinite loop */
-      if (loopcnt++)
-         return(-EINVAL);
-      goto aga_calculate_timings;
-   }
-   hscroll -= ddfstrt;
-   ddfstop = ddfstrt+ddfmax-ddfmin-(64<<clk_shift);
-
-   /*
-    *    Bitplane calculations
-    */
-
-   if (amifb_ilbm) {
-      par->next_plane = div8(par->vxres);
-      par->next_line = par->bpp*par->next_plane;
-   } else {
-      par->next_line = div8(par->vxres);
-      par->next_plane = par->vyres*par->next_line;
-   }
-   par->bplpt0 = ZTWO_PADDR((u_long)videomemory+div8(ddfmin>>clk_shift)+
-                            par->yoffset*par->next_line);
-   par->bpl1mod = par->next_line-div8((ddfmax-ddfmin)>>clk_shift);
-   par->bpl2mod = par->bpl1mod;
-
-   /*
-    *    Hardware Register Values
-    */
-
-   par->bplcon0 = BPC0_COLOR | BPC0_ECSENA | bplpixmode[clk_shift];
-   if (par->bpp == 8)
-      par->bplcon0 |= BPC0_BPU3;
-   else
-      par->bplcon0 |= par->bpp<<12;
-   if (var->nonstd == FB_NONSTD_HAM)
-      par->bplcon0 |= BPC0_HAM;
-   if (var->sync & FB_SYNC_EXT)
-      par->bplcon0 |= BPC0_ERSY;
-   par->bplcon1 = hscroll2hw(hscroll);
-   par->diwstrt = diwstrt2hw(diwstrt_h, diwstrt_v);
-   par->diwstop = diwstop2hw(diwstop_h, diwstop_v);
-   par->diwhigh = diw2hw_high(diwstrt_h, diwstrt_v, distop_h, diwstop_v);
-   par->ddfstrt = ddfstrt2hw(ddfstrt);
-   par->ddfstop = ddfstop2hw(ddfstop);
-   par->fmode = FMODE_SPAGEM | FMODE_SPR32 | FMODE_BPAGEM | FMODE_BPL32;
-
-   switch (par->vmode & FB_VMODE_MASK) {
-      case FB_VMODE_INTERLACED:
-         par->bpl1mod += par->next_line;
-         par->bpl2mod += par->next_line;
-         par->bplcon0 |= BPC0_LACE;
-         break;
-      case FB_VMODE_DOUBLE:
-         par->bpl1mod -= par->next_line;
-         par->fmode |= FMODE_SSCAN2 | FMODE_BSCAN2;
-         break;
-   }
-
-   if (hrate < hfmin || hrate > hfmax || vrate < vfmin || vrate > vfmax)
-      return(-EINVAL);
-
-   return(0);
-}
-
-
-   /*
-    *    Fill the `var' structure based on the values in `par' and maybe
-    *    other values read out of the hardware.
-    */
+	amiga_fb_set_var(&amiga_fb_predefined[0], 0);
 
-static int aga_encode_var(struct fb_var_screeninfo *var,
-                          struct amiga_fb_par *par)
-{
-   u_short clk_shift, line_shift_incd;
-   u_long left, right, upper, lower, hslen, vslen;
-   u_short diwstop_h, diwstop_v;
-   u_short hsstrt, vsstrt, hsstop, vsstop, htotal;
-   int i;
-
-   var->xres = par->xres;
-   var->yres = par->yres;
-   var->xres_virtual = par->vxres;
-   var->yres_virtual = par->vyres;
-   var->xoffset = par->xoffset;
-   var->yoffset = par->yoffset;
-
-   var->bits_per_pixel = par->bpp;
-   var->grayscale = 0;
-
-   var->red.offset = 0;
-   var->red.length = 8;
-   var->red.msb_right = 0;
-   var->blue = var->green = var->red;
-   var->transp.offset = 0;
-   var->transp.length = 0;
-   var->transp.msb_right = 0;
-
-   if (par->bplcon0 & BPC0_HAM)
-      var->nonstd = FB_NONSTD_HAM;
-   else
-      var->nonstd = 0;
-   var->activate = 0;
-
-   var->height = -1;
-   var->width = -1;
-   var->accel = FB_ACCEL_NONE;
-
-   clk_shift = par->clk_shift;
-   var->pixclock = pixclock[clk_shift];
-
-   diwstop_h = hw2diwstop_h(par->diwstop, par->diwhigh);
-   if (par->beamcon0 & BMC0_VARBEAMEN) {
-      hsstrt = hw2hsstrt(par->hsstrt);
-      vsstrt = hw2vsstrt(par->vsstrt);
-      hsstop = hw2hsstop(par->hsstop);
-      vsstop = hw2vsstop(par->vsstop);
-      htotal = hw2htotal(par->htotal);
-      left = par->diwstrt_h-hsstop;
-      right = htotal+hsstrt-diwstop_h+1;
-      hslen = hsstop-hsstrt;
-      upper = par->diwstrt_v-vsstop;
-      lower = vsstrt;
-      vslen = vsstop-vsstrt;
-      var->sync = 0;
-   } else {
-      diwstop_v = hw2diwstop_v(par->diwstop, par->diwhigh);
-      if (par->beamcon0 & BMC0_PAL) {
-         left = par->diwstrt_h-PAL_DIWSTRT_H;
-         right = PAL_DIWSTRT_H+PAL_WINDOW_H-diwstop_h+1;
-         upper = par->diwstrt_v-PAL_DIWSTRT_V;
-         lower = PAL_DIWSTRT_V+PAL_WINDOW_V-diwstop_v;
-      } else {
-         left = par->diwstrt_h-NTSC_DIWSTRT_H;
-         right = NTSC_DIWSTRT_H+NTSC_WINDOW_H-diwstop_h;
-         upper = par->diwstrt_v-NTSC_DIWSTRT_V;
-         lower = NTSC_DIWSTRT_V+NTSC_WINDOW_V-diwstop_v;
-      }
-      hslen = 0;
-      vslen = 0;
-      var->sync = FB_SYNC_BROADCAST;
-   }
-
-   if (par->bplcon0 & BPC0_ERSY)
-      var->sync |= FB_SYNC_EXT;
-   if (par->beamcon0 & BMC0_HSYTRUE)
-      var->sync |= FB_SYNC_HOR_HIGH_ACT;
-   if (par->beamcon0 & BMC0_VSYTRUE)
-      var->sync |= FB_SYNC_VERT_HIGH_ACT;
-   if (par->beamcon0 & BMC0_CSYTRUE)
-      var->sync |= FB_SYNC_COMP_HIGH_ACT;
-
-   switch (par->vmode & FB_VMODE_MASK) {
-      case FB_VMODE_NONINTERLACED:
-         line_shift_incd = 1;
-         break;
-      case FB_VMODE_INTERLACED:
-         line_shift_incd = 0;
-         break;
-      case FB_VMODE_DOUBLE:
-         line_shift_incd = 2;
-         break;
-   }
-
-   var->left_margin = left>>clk_shift;
-   var->right_margin = right>>clk_shift;
-   var->upper_margin = upper<<1>>line_shift_incd;
-   var->lower_margin = lower<<1>>line_shift_incd;
-   var->hsync_len = hslen>>clk_shift;
-   var->vsync_len = vslen<<1>>line_shift_incd;
-   var->vmode = par->vmode;
-   for (i = 0; i < arraysize(var->reserved); i++)
-      var->reserved[i] = 0;
-
-   return(0);
-}
-
-
-   /*
-    *    Read a single color register and split it into
-    *    colors/transparent. Return != 0 for invalid regno.
-    */
+	return &fb_info;
+}
 
-static int aga_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
-                         u_int *transp)
+static int amifbcon_switch(int con)
 {
-   if (regno > 255)
-      return(1);
+	/* Do we have to save the colormap? */
+	if (disp[currcon].cmap.len)
+		do_fb_get_cmap(&disp[currcon].cmap, &disp[currcon].var, 1);
 
-   *red = palette[regno].red;
-   *green = palette[regno].green;
-   *blue = palette[regno].blue;
-   return(0);
+	currcon = con;
+	ami_set_var(&disp[con].var);
+	/* Install new colormap */
+	do_install_cmap(con);
+	return 0;
 }
 
+	/*
+	 * Update the `var' structure (called by amicon.c)
+	 */
 
-   /*
-    *    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 aga_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-                         u_int transp)
+static int amifbcon_updatevar(int con)
 {
-   u_short bplcon3 = current_par.bplcon3;
-
-   if (regno > 255)
-      return(1);
+	ami_pan_var(&disp[con].var);
+	return 0;
+}
 
-   /*
-    *    Update the corresponding Hardware Color Register, unless it's Color
-    *    Register 0 and the screen is blanked.
-    *
-    *    The cli()/sti() pair is here to protect bplcon3 from being changed by
-    *    the VBlank interrupt routine.
-    */
-
-   cli();
-   if (regno || !is_blanked) {
-      custom.bplcon3 = bplcon3 | (regno<<8 & 0xe000);
-      custom.color[regno&31] = rgb2hw_high(red, green, blue);
-      custom.bplcon3 = bplcon3 | (regno<<8 & 0xe000) | BPC3_LOCT;
-      custom.color[regno&31] = rgb2hw_low(red, green, blue);
-      custom.bplcon3 = bplcon3;
-   }
-   sti();
-
-   palette[regno].red = red;
-   palette[regno].green = green;
-   palette[regno].blue = blue;
-
-   return(0);
-}
-
-
-   /*
-    *    Pan or Wrap the Display
-    *
-    *    This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
-    *    in `var'.
-    */
-
-static int aga_pan_display(struct fb_var_screeninfo *var,
-                           struct amiga_fb_par *par)
-{
-   int xoffset, yoffset, vmode, xres_n, xoffset_n;
-   u_short clk_shift, line_shift_incd;
-   u_long ddfmin, ddfmax, ddfstrt, ddfstop, hscroll;
-
-   xoffset = var->xoffset;
-   yoffset = var->yoffset;
-   if (var->vmode & FB_VMODE_YWRAP) {
-      if (hw2ddfstrt(par->ddfstrt) < (par->bpp-1)*64 || xoffset ||
-          yoffset < 0 || yoffset >= par->yres)
-         return(-EINVAL);
-      vmode = par->vmode | FB_VMODE_YWRAP;
-   } else {
-      if (par->diwstrt_h < 323)
-         xoffset = up64(xoffset);
-      if (xoffset < 0 || xoffset+par->xres > par->vxres ||
-          yoffset < 0 || yoffset+par->yres > par->vyres)
-         return(-EINVAL);
-      vmode = par->vmode & ~FB_VMODE_YWRAP;
-   }
-
-   clk_shift = par->clk_shift;
-   switch (vmode & FB_VMODE_MASK) {
-      case FB_VMODE_NONINTERLACED:
-         line_shift_incd = 1;
-         break;
-      case FB_VMODE_INTERLACED:
-         line_shift_incd = 0;
-         break;
-      case FB_VMODE_DOUBLE:
-         line_shift_incd = 2;
-         break;
-   }
-   xres_n = par->xres<<clk_shift;
-   xoffset_n = xoffset<<clk_shift;
-
-   /*
-    *    DMA timings
-    */
-
-   ddfmin = down64(xoffset_n);
-   ddfmax = up64(xoffset_n+xres_n);
-   hscroll = par->diwstrt_h-68-mod64(xoffset_n);
-   ddfstrt = down64(hscroll);
-   if (ddfstrt < 128)
-      return(-EINVAL);
-   hscroll -= ddfstrt;
-   ddfstop = ddfstrt+ddfmax-ddfmin-(64<<clk_shift);
-   par->bplcon1 = hscroll2hw(hscroll);
-   par->ddfstrt = ddfstrt2hw(ddfstrt);
-   par->ddfstop = ddfstop2hw(ddfstop);
-
-   /*
-    *    Bitplane calculations
-    */
-
-   par->bplpt0 = ZTWO_PADDR((u_long)videomemory+div8(ddfmin>>clk_shift)+
-                            yoffset*par->next_line);
-   par->bpl1mod = par->next_line-div8((ddfmax-ddfmin)>>clk_shift);
-   par->bpl2mod = par->bpl1mod;
-   switch (vmode & FB_VMODE_MASK) {
-      case FB_VMODE_INTERLACED:
-         par->bpl1mod += par->next_line;
-         par->bpl2mod += par->next_line;
-         break;
-      case FB_VMODE_DOUBLE:
-         par->bpl1mod -= par->next_line;
-         break;
-   }
-
-   par->xoffset = var->xoffset = xoffset;
-   par->yoffset = var->yoffset = yoffset;
-   par->vmode = var->vmode = vmode;
-   return(0);
-}
-
-
-   /*
-    *    Change the video mode (called by VBlank interrupt)
-    */
-
-void aga_do_vmode(void)
-{
-   struct amiga_fb_par *par = &current_par;
-
-   /*
-    *    Rebuild the dynamic part of the Copper List and activate the right
-    *    Copper List as soon as possible
-    *
-    *    Make sure we're in a Long Frame if the video mode is interlaced.
-    *    This is always the case if we already were in an interlaced mode,
-    *    since then the VBlank only calls us during a Long Frame.
-    *    But this _is_ necessary if we're switching from a non-interlaced
-    *    to an interlaced mode.
-    */
-
-   if ((par->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
-      custom.vposw = 0x8000;
-      aga_build_clist_dyn(clist_lof, clist_shf, 0, par);
-      custom.cop2lc = (u_short *)ZTWO_PADDR(clist_lof);
-      aga_build_clist_dyn(clist_shf, clist_lof, 1, par);
-   } else {
-      aga_build_clist_dyn(clist_lof, NULL, 0, par);
-      custom.cop2lc = (u_short *)ZTWO_PADDR(clist_lof);
-   }
-
-   /*
-    *    Update the hardware registers
-    */
-
-   if (full_vmode_change) {
-      custom.fmode = par->fmode;
-      custom.beamcon0 = par->beamcon0;
-      if (par->beamcon0 & BMC0_VARBEAMEN) {
-         custom.htotal = par->htotal;
-         custom.vtotal = par->vtotal;
-         custom.hsstrt = par->hsstrt;
-         custom.hsstop = par->hsstop;
-         custom.hbstrt = par->hsstrt;
-         custom.hbstop = par->hsstop;
-         custom.vsstrt = par->vsstrt;
-         custom.vsstop = par->vsstop;
-         custom.vbstrt = par->vsstrt;
-         custom.vbstop = par->vsstop;
-         custom.hcenter = par->hcenter;
-      }
-      custom.bplcon3 = par->bplcon3;
-      full_vmode_change = 0;
-
-      /*
-       *    The minimum period for audio depends on htotal (for OCS/ECS/AGA)
-       */
-
-      if (boot_info.bi_amiga.chipset != CS_STONEAGE)
-         amiga_audio_min_period = (par->htotal>>1)+1;
-   }
-   custom.ddfstrt = par->ddfstrt;
-   custom.ddfstop = par->ddfstop;
-   custom.bpl1mod = par->bpl1mod;
-   custom.bpl2mod = par->bpl2mod;
-   custom.bplcon1 = par->bplcon1;
-
-   /*
-    *    Update the Frame Header Copper List
-    */
-
-   aga_update_clist_hdr(clist_hdr, par);
-}
-
-
-   /*
-    *    (Un)Blank the screen (called by VBlank interrupt)
-    */
-
-void aga_do_blank(int blank)
-{
-   struct amiga_fb_par *par = &current_par;
-   u_short bplcon3 = par->bplcon3;
-   u_char red, green, blue;
-
-   if (blank) {
-      custom.dmacon = DMAF_RASTER | DMAF_SPRITE;
-      red = green = blue = 0;
-      if (pwrsave && par->beamcon0 & BMC0_VARBEAMEN) {
-         /* VESA suspend mode, switch off HSYNC */
-         custom.hsstrt = par->htotal+2;
-         custom.hsstop = par->htotal+2;
-      }
-   } else {
-      custom.dmacon = DMAF_SETCLR | DMAF_RASTER;
-      red = palette[0].red;
-      green = palette[0].green;
-      blue = palette[0].blue;
-      if (pwrsave && par->beamcon0 & BMC0_VARBEAMEN) {
-         custom.hsstrt = par->hsstrt;
-         custom.hsstop = par->hsstop;
-      }
-   }
-   custom.bplcon3 = bplcon3;
-   custom.color[0] = rgb2hw_high(red, green, blue);
-   custom.bplcon3 = bplcon3 | BPC3_LOCT;
-   custom.color[0] = rgb2hw_low(red, green, blue);
-   custom.bplcon3 = bplcon3;
-
-   is_blanked = blank;
-}
-
-
-   /*
-    *    Move the cursor (called by VBlank interrupt)
-    */
-
-void aga_do_movecursor(void)
-{
-   struct amiga_fb_par *par = &current_par;
-   struct aga_cursorsprite *sprite = (struct aga_cursorsprite *)lofsprite;
-   long hs, vs, ve;
-   u_short s1, s2, is_double = 0;
-
-   if (par->crsr_x <= -64 || par->crsr_x >= par->xres || par->crsr_y <= -64 ||
-       par->crsr_y >= par->yres)
-      hs = vs = ve = 0;
-   else {
-      hs = par->diwstrt_h-4+(par->crsr_x<<par->clk_shift);
-      vs = par->crsr_y;
-      ve = min(vs+64, par->yres);
-      switch (par->vmode & FB_VMODE_MASK) {
-         case FB_VMODE_INTERLACED:
-            vs >>= 1;
-            ve >>= 1;
-            break;
-         case FB_VMODE_DOUBLE:
-            vs <<= 1;
-            ve <<= 1;
-            is_double = 1;
-            break;
-      }
-      vs += par->diwstrt_v;
-      ve += par->diwstrt_v;
-   }
-   s1 = spr2hw_pos(vs, hs);
-   if (is_double)
-      s1 |= 0x80;
-   s2 = spr2hw_ctl(vs, hs, ve);
-   sprite->sprpos = s1;
-   sprite->sprctl = s2;
-
-   /*
-    *    TODO: Special cases:
-    *    
-    *      - Interlaced: fill position in in both lofsprite & shfsprite
-    *                    swap lofsprite & shfsprite on odd lines
-    *    
-    *      - Doublescan: OK?
-    *    
-    *      - ve <= bottom of display: OK?
-    */
-}
-
-
-   /*
-    *    Flash the cursor (called by VBlank interrupt)
-    */
+	/*
+	 * Blank the display.
+	 */
 
-void aga_do_flashcursor(void)
+static void amifbcon_blank(int blank)
 {
-#if 1
-   static int cursorcount = 0;
-   static int cursorstate = 0;
-
-   switch (cursormode) {
-      case FB_CURSOR_OFF:
-         custom.dmacon = DMAF_SPRITE;
-         break;
-      case FB_CURSOR_ON:
-         custom.dmacon = DMAF_SETCLR | DMAF_SPRITE;
-         break;
-      case FB_CURSOR_FLASH:
-         if (cursorcount)
-            cursorcount--;
-         else {
-            cursorcount = CRSR_RATE;
-            if ((cursorstate = !cursorstate))
-               custom.dmacon = DMAF_SETCLR | DMAF_SPRITE;
-            else
-               custom.dmacon = DMAF_SPRITE;
-         }
-         break;
-   }
-#endif
+	do_blank = blank ? blank : -1;
 }
 
+/* ---------------------------- Generic routines ---------------------------- */
 
-#if 1
-static int aga_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con)
+static struct fb_cmap *get_default_colormap(int bpp)
 {
-#if 0
-   if (ddfstrt >= 192) {
-#endif
-      fix->crsr_width = 64;
-      fix->crsr_height = 64;
-      fix->crsr_xsize = 64;
-      fix->crsr_ysize = 64;
-      fix->crsr_color1 = 17;
-      fix->crsr_color2 = 18;
-#if 0
-   } else {
-      fix->crsr_width = 0;
-      fix->crsr_height = 0;
-      fix->crsr_xsize = 0;
-      fix->crsr_ysize = 0;
-      fix->crsr_color1 = 0;
-      fix->crsr_color2 = 0;
-   }
-#endif
-   return(0);
+	switch (bpp) {
+		case 1:
+			return &default_2_colors;
+			break;
+		case 2:
+			return &default_4_colors;
+			break;
+		case 3:
+			return &default_8_colors;
+			break;
+		default:
+			return &default_16_colors;
+			break;
+	}
 }
 
+#define CNVT_TOHW(val,width)	((((val)<<(width))+0x7fff-(val))>>16)
+#define CNVT_FROMHW(val,width)	(((width) ? ((((val)<<16)-(val)) / \
+                                            ((1<<(width))-1)) : 0))
 
-static int aga_get_var_cursorinfo(struct fb_var_cursorinfo *var, int con)
+static int do_fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var,
+                          int kspc)
 {
-   struct aga_cursorsprite *sprite = (struct aga_cursorsprite *)lofsprite;
+	int i, start;
+	u_short *red, *green, *blue, *transp;
+	u_int hred, hgreen, hblue, htransp;
 
-   /* TODO: interlaced sprites */
-   memcpy(var->data, sprite->u.nonlaced.data, sizeof(var->data));
-   return(0);
+	red = cmap->red;
+	green = cmap->green;
+	blue = cmap->blue;
+	transp = cmap->transp;
+	start = cmap->start;
+	if (start < 0)
+		return -EINVAL;
+	for (i = 0; i < cmap->len; i++) {
+		if (ami_getcolreg(start++, &hred, &hgreen, &hblue, &htransp))
+			return 0;
+		hred = CNVT_FROMHW(hred, var->red.length);
+		hgreen = CNVT_FROMHW(hgreen, var->green.length);
+		hblue = CNVT_FROMHW(hblue, var->blue.length);
+		htransp = CNVT_FROMHW(htransp, var->transp.length);
+		if (kspc) {
+			*red = hred;
+			*green = hgreen;
+			*blue = hblue;
+			if (transp)
+				*transp = htransp;
+		} else {
+			put_fs_word(hred, red);
+			put_fs_word(hgreen, green);
+			put_fs_word(hblue, blue);
+			if (transp)
+				put_fs_word(htransp, transp);
+		}
+		red++;
+		green++;
+		blue++;
+		if (transp)
+			transp++;
+	}
+	return 0;
 }
 
-
-static int aga_set_var_cursorinfo(struct fb_var_cursorinfo *var, int con)
+static int do_fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var,
+                          int kspc)
 {
-   struct aga_cursorsprite *sprite = (struct aga_cursorsprite *)lofsprite;
+	int i, start;
+	u_short *red, *green, *blue, *transp;
+	u_int hred, hgreen, hblue, htransp;
 
-   /* TODO: interlaced sprites */
-   memcpy(sprite->u.nonlaced.data, var->data, sizeof(var->data));
-   return(0);
-}
+	red = cmap->red;
+	green = cmap->green;
+	blue = cmap->blue;
+	transp = cmap->transp;
+	start = cmap->start;
 
+	if (start < 0)
+		return -EINVAL;
+	for (i = 0; i < cmap->len; i++) {
+		if (kspc) {
+			hred = *red;
+			hgreen = *green;
+			hblue = *blue;
+			htransp = transp ? *transp : 0;
+		} else {
+			hred = get_fs_word(red);
+			hgreen = get_fs_word(green);
+			hblue = get_fs_word(blue);
+			htransp = transp ? get_fs_word(transp) : 0;
+		}
+		hred = CNVT_TOHW(hred, var->red.length);
+		hgreen = CNVT_TOHW(hgreen, var->green.length);
+		hblue = CNVT_TOHW(hblue, var->blue.length);
+		htransp = CNVT_TOHW(htransp, var->transp.length);
+		red++;
+		green++;
+		blue++;
+		if (transp)
+			transp++;
+		if (ami_setcolreg(start++, hred, hgreen, hblue, htransp))
+			return 0;
+	}
+	return 0;
+}
 
-static int aga_get_cursorstate(struct fb_cursorstate *state, int con)
+static void do_install_cmap(int con)
 {
-   state->xoffset = current_par.crsr_x;
-   state->yoffset = current_par.crsr_y;
-   state->mode = cursormode;
-   return(0);
+	if (con != currcon)
+		return;
+	if (disp[con].cmap.len)
+		do_fb_set_cmap(&disp[con].cmap, &disp[con].var, 1);
+	else
+		do_fb_set_cmap(get_default_colormap(disp[con].var.bits_per_pixel),
+		                                    &disp[con].var, 1);
 }
 
-
-static int aga_set_cursorstate(struct fb_cursorstate *state, int con)
+static void memcpy_fs(int fsfromto, void *to, void *from, int len)
 {
-   current_par.crsr_x = state->xoffset;
-   current_par.crsr_y = state->yoffset;
-   cursormode = state->mode;
-   do_movecursor = 1;
-   return(0);
+	switch (fsfromto) {
+		case 0:
+			memcpy(to, from, len);
+			return;
+		case 1:
+			memcpy_fromfs(to, from, len);
+			return;
+		case 2:
+			memcpy_tofs(to, from, len);
+			return;
+	}
 }
-#endif
 
+static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto)
+{
+	int size;
+	int tooff = 0, fromoff = 0;
 
-   /*
-    *    Build the Frame Header Copper List
-    */
-
-static __inline__ void aga_build_clist_hdr(struct clist_hdr *cop)
-{
-   int i, j;
-   u_long p;
-
-   cop->bplcon0.l = CMOVE(BPC0_COLOR | BPC0_SHRES | BPC0_ECSENA, bplcon0);
-   cop->diwstrt.l = CMOVE(0x0181, diwstrt);
-   cop->diwstop.l = CMOVE(0x0281, diwstop);
-   cop->diwhigh.l = CMOVE(0x0000, diwhigh);
-   for (i = 0; i < 8; i++)
-      cop->sprfix[i].l = CMOVE(0, spr[i].pos);
-   for (i = 0, j = 0; i < 8; i++) {
-      p = ZTWO_PADDR(dummysprite);
-      cop->sprstrtup[j++].l = CMOVE(highw(p), sprpt[i]);
-      cop->sprstrtup[j++].l = CMOVE2(loww(p), sprpt[i]);
-   }
-   cop->wait.l = CWAIT(0, 12);         /* Initial value */
-   cop->jump.l = CMOVE(0, copjmp2);
-   cop->wait_forever.l = CEND;
-}
-
-
-   /*
-    *    Update the Frame Header Copper List
-    */
-
-static __inline__ void aga_update_clist_hdr(struct clist_hdr *cop,
-                                            struct amiga_fb_par *par)
-{
-   cop->bplcon0.l = CMOVE(~(BPC0_BPU3 | BPC0_BPU2 | BPC0_BPU1 | BPC0_BPU0) &
-                          par->bplcon0, bplcon0);
-   cop->wait.l = CWAIT(0, par->diwstrt_v-2);
-}
-
-
-   /*
-    *    Build the Long Frame/Short Frame Copper List
-    */
-
-static void aga_build_clist_dyn(struct clist_dyn *cop,
-                                struct clist_dyn *othercop, u_short shf,
-                                struct amiga_fb_par *par)
-{
-   u_long y_wrap, bplpt0, p, line;
-   int i, j = 0;
-
-   cop->diwstrt.l = CMOVE(par->diwstrt, diwstrt);
-   cop->diwstop.l = CMOVE(par->diwstop, diwstop);
-   cop->diwhigh.l = CMOVE(par->diwhigh, diwhigh);
-   cop->bplcon0.l = CMOVE(par->bplcon0, bplcon0);
-
-   /* Point Sprite 0 at cursor sprite */
-
-   /* TODO: This should depend on the vertical sprite position too */
-   if (shf) {
-      p = ZTWO_PADDR(shfsprite);
-      cop->sprpt[0].l = CMOVE(highw(p), sprpt[0]);
-      cop->sprpt[1].l = CMOVE2(loww(p), sprpt[0]);
-   } else {
-      p = ZTWO_PADDR(lofsprite);
-      cop->sprpt[0].l = CMOVE(highw(p), sprpt[0]);
-      cop->sprpt[1].l = CMOVE2(loww(p), sprpt[0]);
-   }
-
-   bplpt0 = par->bplpt0;
-   if (shf)
-      bplpt0 += par->next_line;
-   y_wrap = par->vmode & FB_VMODE_YWRAP ? par->yoffset : 0;
-
-   /* Set up initial bitplane ptrs */
-
-   for (i = 0, p = bplpt0; i < par->bpp; i++, p += par->next_plane) {
-      cop->rest[j++].l = CMOVE(highw(p), bplpt[i]);
-      cop->rest[j++].l = CMOVE2(loww(p), bplpt[i]);
-   }
-
-   if (y_wrap) {
-      bplpt0 -= y_wrap*par->next_line;
-      line = par->yres-y_wrap;
-      switch (par->vmode & FB_VMODE_MASK) {
-         case FB_VMODE_INTERLACED:
-            line >>= 1;
-            break;
-         case FB_VMODE_DOUBLE:
-            line <<= 1;
-            break;
-      }
-      line += par->diwstrt_v;
-
-      /* Handle skipping over 256-line chunks */
-
-      while (line > 256) {
-         /* Hardware limitation - 8 bit counter */
-         cop->rest[j++].l = CWAIT(par->htotal-4, 255);
-         /* Wait(0, 0) - make sure we're in the new segment */
-         cop->rest[j++].l = CWAIT(0, 0);
-         line -= 256;
-      }
-      cop->rest[j++].l = CWAIT(par->htotal-11, line-1);
-
-      for (i = 0, p = bplpt0; i < par->bpp; i++, p += par->next_plane) {
-         cop->rest[j++].l = CMOVE(highw(p), bplpt[i]);
-         cop->rest[j++].l = CMOVE2(loww(p), bplpt[i]);
-      }
-   }
-
-   if (othercop) {
-      p = ZTWO_PADDR(othercop);
-      cop->rest[j++].l = CMOVE(highw(p), cop2lc);
-      cop->rest[j++].l = CMOVE2(loww(p), cop2lc);
-   }
+	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)
+		return;
+	size *= sizeof(u_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);
+}
 
-   /* End of Copper list */
-   cop->rest[j++].l = CEND;
+static int alloc_cmap(struct fb_cmap *cmap, int len, int transp)
+{
+	int size = len*sizeof(u_short);
 
-   if (j > arraysize(cop->rest))
-      printk("aga_build_clist_dyn: copper list overflow (%d entries)\n", j);
+	if (cmap->len != len) {
+		if (cmap->red)
+			kfree(cmap->red);
+		if (cmap->green)
+			kfree(cmap->green);
+		if (cmap->blue)
+			kfree(cmap->blue);
+		if (cmap->transp)
+			kfree(cmap->transp);
+		cmap->red = cmap->green = cmap->blue = cmap->transp = NULL;
+		cmap->len = 0;
+		if (!len)
+			return 0;
+		if (!(cmap->red = kmalloc(size, GFP_ATOMIC)))
+			return -1;
+		if (!(cmap->green = kmalloc(size, GFP_ATOMIC)))
+			return -1;
+		if (!(cmap->blue = kmalloc(size, GFP_ATOMIC)))
+			return -1;
+		if (transp) {
+			if (!(cmap->transp = kmalloc(size, GFP_ATOMIC)))
+				return -1;
+		} else
+			cmap->transp = NULL;
+	}
+	cmap->start = 0;
+	cmap->len = len;
+	copy_cmap(get_default_colormap(len), cmap, 0);
+	return 0;
 }
-#endif /* CONFIG_AMIFB_AGA */
 
+static int flash_cursor(void)
+{
+	static int cursorcount = 1;
 
-/* -------------------- Interfaces to hardware functions -------------------- */
+	if (cursormode == FB_CURSOR_FLASH) {
+		if (!--cursorcount) {
+			cursorstate = -cursorstate;
+			cursorcount = cursorrate;
+			if (!is_blanked)
+				return 1;
+		}
+	}
+	return 0;
+}
 
+	/*
+	 * VBlank Display Interrupt
+	 */
 
-#ifdef CONFIG_AMIFB_OCS
-static struct fb_hwswitch ocs_switch = {
-   ocs_init, ocs_encode_fix, ocs_decode_var, ocs_encode_var, ocs_getcolreg,
-   ocs_setcolreg, ocs_pan_display, ocs_do_vmode, ocs_do_blank,
-   ocs_do_movecursor, ocs_do_flashcursor
-};
-#endif /* CONFIG_AMIFB_OCS */
+static void amifb_interrupt(int irq, void *dev_id, struct pt_regs *fp)
+{
+	u_short ints = custom.intreqr & custom.intenar;
+	static struct irq_server server = {0, 0};
 
-#ifdef CONFIG_AMIFB_ECS
-static struct fb_hwswitch ecs_switch = {
-   ecs_init, ecs_encode_fix, ecs_decode_var, ecs_encode_var, ecs_getcolreg,
-   ecs_setcolreg, ecs_pan_display, ecs_do_vmode, ecs_do_blank,
-   ecs_do_movecursor, ecs_do_flashcursor
-};
-#endif /* CONFIG_AMIFB_ECS */
+	if (ints & IF_BLIT) {
+		custom.intreq = IF_BLIT;
+		amiga_do_irq(IRQ_IDX(IRQ_AMIGA_BLIT), fp);
+	}
+
+	if (ints & IF_COPER) {
+		custom.intreq = IF_COPER;
+		if (do_vmode_pan || do_vmode_full)
+			ami_update_display();
+
+		if (do_vmode_full)
+			ami_init_display();
+
+		if (do_vmode_pan) {
+			flash_cursor();
+			ami_rebuild_copper();
+			do_cursor = do_vmode_pan = 0;
+		} else if (do_cursor) {
+			flash_cursor();
+			ami_set_sprite();
+			do_cursor = 0;
+		} else {
+			if (flash_cursor())
+				ami_set_sprite();
+		}
 
-#ifdef CONFIG_AMIFB_AGA
-static struct fb_hwswitch aga_switch = {
-   aga_init, aga_encode_fix, aga_decode_var, aga_encode_var, aga_getcolreg,
-   aga_setcolreg, aga_pan_display, aga_do_vmode, aga_do_blank,
-   aga_do_movecursor, aga_do_flashcursor
-};
-#endif /* CONFIG_AMIFB_AGA */
+		if (get_vbpos() < down2(currentpar.diwstrt_v - 4))
+			custom.copjmp2 = 0;
 
+		if (do_blank) {
+			ami_do_blank();
+			do_blank = 0;
+		}
 
-/* -------------------- Generic routines ------------------------------------ */
+		if (do_vmode_full) {
+			ami_reinit_copper();
+			do_vmode_full = 0;
+		}
+		amiga_do_irq_list(IRQ_IDX(IRQ_AMIGA_VERTB), fp, &server);
+	}
 
+	if (ints & IF_VERTB) {
+		printk("%s: Warning: IF_VERTB was enabled\n", __FUNCTION__);
+		custom.intena = IF_VERTB;
+	}
+}
 
-   /*
-    *    Allocate, Clear and Align a Block of Chip Memory
-    */
+	/*
+	 * Get a Video Modes
+	 */
 
-static u_long chipalloc(u_long size)
+static void get_video_mode(const char *name)
 {
-   u_long ptr;
-
-   size += PAGE_SIZE-1;
-   if (!(ptr = (u_long)amiga_chip_alloc(size)))
-      panic("No Chip RAM for frame buffer");
-   memset((void *)ptr, 0, size);
-   ptr = PAGE_ALIGN(ptr);
+	int i;
 
-   return(ptr);
+	for (i = 1; i < NUM_PREDEF_MODES; i++) {
+		if (!strcmp(name, amiga_fb_modenames[i])) {
+			amiga_fb_predefined[0] = amiga_fb_predefined[i];
+			amifb_usermode = i;
+			return;
+		}
+	}
 }
 
+	/*
+	 * Probe the  Video Modes
+	 */
 
-   /*
-    *    Fill the hardware's `par' structure.
-    */
-
-static void amiga_fb_get_par(struct amiga_fb_par *par)
+static void check_default_mode(void)
 {
-   if (current_par_valid)
-      *par = current_par;
-   else
-      fbhw->decode_var(&amiga_fb_predefined[amifb_mode], par);
+	struct amiga_fb_par par;
+	int mode;
+
+	for (mode = 0; mode < NUM_PREDEF_MODES; mode++) {
+		if (!ami_decode_var(&amiga_fb_predefined[mode], &par)) {
+			if (mode)
+				amiga_fb_predefined[0] = amiga_fb_predefined[mode];
+			return;
+		}
+		if (!mode)
+			printk("Can't use default video mode. Probing video modes...\n");
+	}
+	panic("Can't find any usable video mode");
 }
 
+	/*
+	 * Allocate, Clear and Align a Block of Chip Memory
+	 */
 
-static void amiga_fb_set_par(struct amiga_fb_par *par)
+static u_long chipalloc(u_long size)
 {
-   do_vmode = 0;
-   current_par = *par;
-   full_vmode_change = 1;
-   do_vmode = 1;
-   current_par_valid = 1;
+	u_long ptr;
+
+	size += PAGE_SIZE-1;
+	if (!(ptr = (u_long)amiga_chip_alloc(size)))
+		panic("No Chip RAM for frame buffer");
+	memset((void *)ptr, 0, size);
+	ptr = PAGE_ALIGN(ptr);
+
+	return ptr;
 }
 
+	/*
+	 * A strtok which returns empty strings, too
+	 */
 
-static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
+static char *strtoke(char *s,const char *ct)
 {
-   int err, activate;
-   struct amiga_fb_par par;
+	char *sbegin, *send;
+	static char *ssave = NULL;
 
-   if ((err = fbhw->decode_var(var, &par)))
-      return(err);
-   activate = var->activate;
-   if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive)
-      amiga_fb_set_par(&par);
-   fbhw->encode_var(var, &par);
-   var->activate = activate;
-   return(0);
+	sbegin  = s ? s : ssave;
+	if (!sbegin)
+		return NULL;
+	if (*sbegin == '\0') {
+		ssave = NULL;
+		return NULL;
+	}
+	send = strpbrk(sbegin, ct);
+	if (send && *send != '\0')
+		*send++ = '\0';
+	ssave = send;
+	return sbegin;
 }
 
+/* --------------------------- Hardware routines --------------------------- */
 
-   /*
-    *    Default Colormaps
-    */
-
-static u_short red2[] =
-   { 0x0000, 0xc000 };
-static u_short green2[] =
-   { 0x0000, 0xc000 };
-static u_short blue2[] =
-   { 0x0000, 0xc000 };
+	/*
+	 * This function should fill in the `fix' structure based on the
+	 * values in the `par' structure.
+	 */
 
-static u_short red4[] =
-   { 0x0000, 0xc000, 0x8000, 0xffff };
-static u_short green4[] =
-   { 0x0000, 0xc000, 0x8000, 0xffff };
-static u_short blue4[] =
-   { 0x0000, 0xc000, 0x8000, 0xffff };
+static int ami_encode_fix(struct fb_fix_screeninfo *fix,
+                          struct amiga_fb_par *par)
+{
+	int i;
 
-static u_short red8[] =
-   { 0x0000, 0x0000, 0x0000, 0x0000, 0xc000, 0xc000, 0xc000, 0xc000 };
-static u_short green8[] =
-   { 0x0000, 0x0000, 0xc000, 0xc000, 0x0000, 0x0000, 0xc000, 0xc000 };
-static u_short blue8[] =
-   { 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000 };
+	strcpy(fix->id, amiga_fb_name);
+	fix->smem_start = videomemory;
+	fix->smem_len = videomemorysize;
+
+	if (amifb_ilbm) {
+		fix->type = FB_TYPE_INTERLEAVED_PLANES;
+		fix->type_aux = par->next_line;
+	} else {
+		fix->type = FB_TYPE_PLANES;
+		fix->type_aux = 0;
+	}
+	fix->line_length = div8(upx(16<<maxfmode, par->vxres));
+	fix->visual = FB_VISUAL_PSEUDOCOLOR;
 
-static u_short red16[] =
-   { 0x0000, 0x0000, 0x0000, 0x0000, 0xc000, 0xc000, 0xc000, 0xc000,
-     0x8000, 0x0000, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff };
-static u_short green16[] =
-   { 0x0000, 0x0000, 0xc000, 0xc000, 0x0000, 0x0000, 0xc000, 0xc000,
-     0x8000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0xffff, 0xffff };
-static u_short blue16[] =
-   { 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000, 0x0000, 0xc000,
-     0x8000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff, 0x0000, 0xffff };
+	if (par->vmode & FB_VMODE_YWRAP) {
+		fix->ywrapstep = 1;
+		fix->xpanstep = fix->ypanstep = 0;
+	} else {
+		fix->ywrapstep = 0;
+		if (par->vmode &= FB_VMODE_SMOOTH_XPAN)
+			fix->xpanstep = 1;
+		else
+			fix->xpanstep = 16<<maxfmode;
+		fix->ypanstep = 1;
+	}
 
+	for (i = 0; i < arraysize(fix->reserved); i++)
+		fix->reserved[i] = 0;
 
-static struct fb_cmap default_2_colors =
-   { 0, 2, red2, green2, blue2, NULL };
-static struct fb_cmap default_8_colors =
-   { 0, 8, red8, green8, blue8, NULL };
-static struct fb_cmap default_4_colors =
-   { 0, 4, red4, green4, blue4, NULL };
-static struct fb_cmap default_16_colors =
-   { 0, 16, red16, green16, blue16, NULL };
+	return 0;
+}
 
+	/*
+	 * Get the video params out of `var'. If a value doesn't fit, round
+	 * it up, if it's too big, return -EINVAL.
+	 */
 
-static struct fb_cmap *get_default_colormap(int bpp)
+static int ami_decode_var(struct fb_var_screeninfo *var,
+                          struct amiga_fb_par *par)
 {
-   switch (bpp) {
-      case 1:
-         return(&default_2_colors);
-         break;
-      case 2:
-         return(&default_4_colors);
-         break;
-      case 3:
-         return(&default_8_colors);
-         break;
-      default:
-         return(&default_16_colors);
-         break;
-   }
-}
+	u_short clk_shift, line_shift;
+	u_long maxfetchstop, fstrt, fsize, fconst, xres_n, yres_n;
+	u_long hrate = 0, vrate = 0;
 
+	/*
+	 * Find a matching Pixel Clock
+	 */
 
-#define CNVT_TOHW(val,width)     ((((val)<<(width))+0x7fff-(val))>>16)
-#define CNVT_FROMHW(val,width)   (((width) ? ((((val)<<16)-(val)) / \
-                                              ((1<<(width))-1)) : 0))
+	for (clk_shift = TAG_SHRES; clk_shift < TAG_LORES; clk_shift++)
+		if (var->pixclock <= pixclock[clk_shift])
+			break;
+	if (clk_shift >= TAG_LORES)
+		return -EINVAL;
+	par->clk_shift = clk_shift;
 
-static int do_fb_get_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var,
-                          int kspc)
-{
-   int i, start;
-   u_short *red, *green, *blue, *transp;
-   u_int hred, hgreen, hblue, htransp;
-
-   red = cmap->red;
-   green = cmap->green;
-   blue = cmap->blue;
-   transp = cmap->transp;
-   start = cmap->start;
-   if (start < 0)
-      return(-EINVAL);
-   for (i = 0; i < cmap->len; i++) {
-      if (fbhw->getcolreg(start++, &hred, &hgreen, &hblue, &htransp))
-         return(0);
-      hred = CNVT_FROMHW(hred, var->red.length);
-      hgreen = CNVT_FROMHW(hgreen, var->green.length);
-      hblue = CNVT_FROMHW(hblue, var->blue.length);
-      htransp = CNVT_FROMHW(htransp, var->transp.length);
-      if (kspc) {
-         *red = hred;
-         *green = hgreen;
-         *blue = hblue;
-         if (transp)
-            *transp = htransp;
-      } else {
-         put_fs_word(hred, red);
-         put_fs_word(hgreen, green);
-         put_fs_word(hblue, blue);
-         if (transp)
-            put_fs_word(htransp, transp);
-      }
-      red++;
-      green++;
-      blue++;
-      if (transp)
-         transp++;
-   }
-   return(0);
-}
+	/*
+	 * Check the Geometry Values
+	 */
 
+	if ((par->xres = var->xres) < 64)
+		return -EINVAL;
+	if ((par->yres = var->yres) < 64)
+		return -EINVAL;
+	if ((par->vxres = var->xres_virtual) < 64)
+		return -EINVAL;
+	if ((par->vyres = var->yres_virtual) < 64)
+		return -EINVAL;
+
+	par->bpp = var->bits_per_pixel;
+	if (!var->nonstd) {
+		if (par->bpp <= 0 || par->bpp > maxdepth[clk_shift])
+			return -EINVAL;
+	} else if (var->nonstd == FB_NONSTD_HAM) {
+		if (par->bpp != 6)
+			if (par->bpp != 8 || !IS_AGA)
+				return -EINVAL;
+	} else
+		return -EINVAL;
 
-static int do_fb_set_cmap(struct fb_cmap *cmap, struct fb_var_screeninfo *var,
-                          int kspc)
-{
-   int i, start;
-   u_short *red, *green, *blue, *transp;
-   u_int hred, hgreen, hblue, htransp;
-
-   red = cmap->red;
-   green = cmap->green;
-   blue = cmap->blue;
-   transp = cmap->transp;
-   start = cmap->start;
-
-   if (start < 0)
-      return(-EINVAL);
-   for (i = 0; i < cmap->len; i++) {
-      if (kspc) {
-         hred = *red;
-         hgreen = *green;
-         hblue = *blue;
-         htransp = transp ? *transp : 0;
-      } else {
-         hred = get_fs_word(red);
-         hgreen = get_fs_word(green);
-         hblue = get_fs_word(blue);
-         htransp = transp ? get_fs_word(transp) : 0;
-      }
-      hred = CNVT_TOHW(hred, var->red.length);
-      hgreen = CNVT_TOHW(hgreen, var->green.length);
-      hblue = CNVT_TOHW(hblue, var->blue.length);
-      htransp = CNVT_TOHW(htransp, var->transp.length);
-      red++;
-      green++;
-      blue++;
-      if (transp)
-         transp++;
-      if (fbhw->setcolreg(start++, hred, hgreen, hblue, htransp))
-         return(0);
-   }
-   return(0);
-}
+	/*
+	 * FB_VMODE_SMOOTH_XPAN will be cleared, if one of the folloing
+	 * checks failed and smooth scrolling is not possible
+	 */
+
+	par->vmode = var->vmode | FB_VMODE_SMOOTH_XPAN;
+	switch (par->vmode & FB_VMODE_MASK) {
+		case FB_VMODE_INTERLACED:
+			line_shift = 0;
+			break;
+		case FB_VMODE_NONINTERLACED:
+			line_shift = 1;
+			break;
+		case FB_VMODE_DOUBLE:
+			if (!IS_AGA)
+				return -EINVAL;
+			line_shift = 2;
+			break;
+		default:
+			return -EINVAL;
+			break;
+	}
+	par->line_shift = line_shift;
+
+	/*
+	 * Vertical and Horizontal Timings
+	 */
 
+	xres_n = par->xres<<clk_shift;
+	yres_n = par->yres<<line_shift;
+	par->htotal = down8((var->left_margin+par->xres+var->right_margin+var->hsync_len)<<clk_shift);
+	par->vtotal = down2(((var->upper_margin+par->yres+var->lower_margin+var->vsync_len)<<line_shift)+1);
+
+	if (IS_AGA)
+		par->bplcon3 = sprpixmode[clk_shift];
+	else
+		par->bplcon3 = 0;
+	if (var->sync & FB_SYNC_BROADCAST) {
+		par->diwstop_h = par->htotal-((var->right_margin-var->hsync_len)<<clk_shift);
+		if (IS_AGA)
+			par->diwstop_h += mod4(var->hsync_len);
+		else
+			par->diwstop_h = down4(par->diwstop_h);
+		par->diwstrt_h = par->diwstop_h - xres_n;
+		par->diwstop_v = par->vtotal-((var->lower_margin-var->vsync_len)<<line_shift);
+		par->diwstrt_v = par->diwstop_v - yres_n;
+		if (par->diwstop_h >= par->htotal+8 || par->diwstop_v > par->vtotal)
+			return -EINVAL;
+		if (!IS_OCS) {
+			/* Initialize sync with some reasonable values for pwrsave */
+			par->hsstrt = 160;
+			par->hsstop = 320;
+			par->vsstrt = 30;
+			par->vsstop = 34;
+		} else {
+			par->hsstrt = 0;
+			par->hsstop = 0;
+			par->vsstrt = 0;
+			par->vsstop = 0;
+		}
+		if (par->vtotal > (PAL_VTOTAL+NTSC_VTOTAL)/2) {
+			/* PAL video mode */
+			if (par->htotal != PAL_HTOTAL)
+				return -EINVAL;
+			if (par->diwstrt_h < PAL_DIWSTRT_H)
+				return -EINVAL;
+			if (par->diwstrt_v < PAL_DIWSTRT_V)
+				return -EINVAL;
+			hrate = 15625;
+			vrate = 50;
+			if (!IS_OCS) {
+				par->beamcon0 = BMC0_PAL;
+				par->bplcon3 |= BPC3_BRDRBLNK;
+			} else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) || 
+			           AMIGAHW_PRESENT(AGNUS_HR_NTSC)) {
+				par->beamcon0 = BMC0_PAL;
+				par->hsstop = 1;
+			} else if (boot_info.bi_un.bi_ami.vblank != 50)
+				return -EINVAL;
+		} else {
+			/* NTSC video mode
+			 * In the AGA chipset seems to be hardware bug with BPC3_BRDRBLNK
+			 * and NTSC activated, so than better let diwstop_h <= 1812
+			 */
+			if (par->htotal != NTSC_HTOTAL)
+				return -EINVAL;
+			if (par->diwstrt_h < NTSC_DIWSTRT_H)
+				return -EINVAL;
+			if (par->diwstrt_v < NTSC_DIWSTRT_V)
+				return -EINVAL;
+			hrate = 15750;
+			vrate = 60;
+			if (!IS_OCS) {
+				par->beamcon0 = 0;
+				par->bplcon3 |= BPC3_BRDRBLNK;
+			} else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) || 
+			           AMIGAHW_PRESENT(AGNUS_HR_NTSC)) {
+				par->beamcon0 = 0;
+				par->hsstop = 1;
+			} else if (boot_info.bi_un.bi_ami.vblank != 60)
+				return -EINVAL;
+		}
+		if (IS_OCS) {
+			if (par->diwstrt_h >= 1024 || par->diwstop_h < 1024 ||
+			    par->diwstrt_v >=  512 || par->diwstop_v <  256)
+				return -EINVAL;
+		}
+	} else if (!IS_OCS) {
+		/* Programmable video mode */
+		par->hsstrt = var->right_margin<<clk_shift;
+		par->hsstop = (var->right_margin+var->hsync_len)<<clk_shift;
+		par->diwstop_h = par->htotal - mod8(par->hsstrt) + 8 - (1 << clk_shift);
+		if (!IS_AGA)
+			par->diwstop_h = down4(par->diwstop_h) - 16;
+		par->diwstrt_h = par->diwstop_h - xres_n;
+		par->hbstop = par->diwstrt_h + 4;
+		par->hbstrt = par->diwstop_h + 4;
+		if (par->hbstrt >= par->htotal + 8)
+			par->hbstrt -= par->htotal;
+		par->hcenter = par->hsstrt + (par->htotal >> 1);
+		par->vsstrt = var->lower_margin<<line_shift;
+		par->vsstop = (var->lower_margin+var->vsync_len)<<line_shift;
+		par->diwstop_v = par->vtotal;
+		if ((par->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED)
+			par->diwstop_v -= 2;
+		par->diwstrt_v = par->diwstop_v - yres_n;
+		par->vbstop = par->diwstrt_v - 2;
+		par->vbstrt = par->diwstop_v - 2;
+		if (par->vtotal > 2048 || par->htotal > 2048)
+			return -EINVAL;
+		par->bplcon3 |= BPC3_EXTBLKEN;
+		par->beamcon0 = BMC0_HARDDIS | BMC0_VARVBEN | BMC0_LOLDIS |
+		                BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARBEAMEN |
+		                BMC0_PAL | BMC0_VARCSYEN;
+		if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+			par->beamcon0 |= BMC0_HSYTRUE;
+		if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+			par->beamcon0 |= BMC0_VSYTRUE;
+		if (var->sync & FB_SYNC_COMP_HIGH_ACT)
+			par->beamcon0 |= BMC0_CSYTRUE;
+		hrate = (amiga_masterclock+par->htotal/2)/par->htotal;
+		vrate = div2(par->vtotal) * par->htotal;
+		vrate = (amiga_masterclock+vrate/2)/vrate;
+	} else
+		return -EINVAL;
 
-static void do_install_cmap(int con)
-{
-   if (con != currcon)
-      return;
-   if (disp[con].cmap.len)
-      do_fb_set_cmap(&disp[con].cmap, &disp[con].var, 1);
-   else
-      do_fb_set_cmap(get_default_colormap(disp[con].var.bits_per_pixel),
-                                          &disp[con].var, 1);
-}
+	/*
+	 * Checking the DMA timing
+	 */
 
+	fconst = 16<<maxfmode<<clk_shift;
 
-static void memcpy_fs(int fsfromto, void *to, void *from, int len)
-{
-   switch (fsfromto) {
-      case 0:
-         memcpy(to, from, len);
-         return;
-      case 1:
-         memcpy_fromfs(to, from, len);
-         return;
-      case 2:
-         memcpy_tofs(to, from, len);
-         return;
-   }
-}
+	/*
+	 * smallest window start value without turn off other dma cycles
+	 * than sprite1-7, unless you change min_fstrt
+	 */
 
 
-static void copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto)
-{
-   int size;
-   int tooff = 0, fromoff = 0;
+	fsize = ((maxfmode+clk_shift <= 1) ? fconst : 64);
+	fstrt = downx(fconst, par->diwstrt_h-4) - fsize;
+	if (fstrt < min_fstrt)
+		return -EINVAL;
 
-   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)
-      return;
-   size *= sizeof(u_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);
-}
+	/*
+	 * smallest window start value where smooth scrolling is possible
+	 */
 
+	fstrt = downx(fconst, par->diwstrt_h-fconst+(1<<clk_shift)-4) - fsize;
+	if (fstrt < min_fstrt)
+		par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
+
+	maxfetchstop = down16(par->htotal - 80);
+
+	fstrt = downx(fconst, par->diwstrt_h-4) - 64 - fconst;
+	fsize = upx(fconst, xres_n + modx(fconst, downx(1<<clk_shift, par->diwstrt_h-4)));
+	if (fstrt + fsize > maxfetchstop)
+		par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
+
+	fsize = upx(fconst, xres_n);
+	if (fstrt + fsize > maxfetchstop)
+		return -EINVAL;
+
+	if (maxfmode + clk_shift <= 1) {
+		fsize = up64(xres_n + fconst - 1);
+		if (min_fstrt + fsize - 64 > maxfetchstop)
+			par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
+
+		fsize = up64(xres_n);
+		if (min_fstrt + fsize - 64 > maxfetchstop)
+			return -EINVAL;
 
-static int alloc_cmap(struct fb_cmap *cmap, int len, int transp)
-{
-   int size = len*sizeof(u_short);
+		fsize -= 64;
+	} else
+		fsize -= fconst;
 
-   if (cmap->len != len) {
-      if (cmap->red)
-         kfree(cmap->red);
-      if (cmap->green)
-         kfree(cmap->green);
-      if (cmap->blue)
-         kfree(cmap->blue);
-      if (cmap->transp)
-         kfree(cmap->transp);
-      cmap->red = cmap->green = cmap->blue = cmap->transp = NULL;
-      cmap->len = 0;
-      if (!len)
-         return(0);
-      if (!(cmap->red = kmalloc(size, GFP_ATOMIC)))
-         return(-1);
-      if (!(cmap->green = kmalloc(size, GFP_ATOMIC)))
-         return(-1);
-      if (!(cmap->blue = kmalloc(size, GFP_ATOMIC)))
-         return(-1);
-      if (transp) {
-         if (!(cmap->transp = kmalloc(size, GFP_ATOMIC)))
-            return(-1);
-      } else
-         cmap->transp = NULL;
-   }
-   cmap->start = 0;
-   cmap->len = len;
-   copy_cmap(get_default_colormap(len), cmap, 0);
-   return(0);
-}
+	/*
+	 * Check if there is enough time to update the bitplane pointers for ywrap
+	 */
 
+	if (par->htotal-fsize-64 < par->bpp*64)
+		par->vmode &= ~FB_VMODE_YWRAP;
 
-   /*
-    *    Get the Fixed Part of the Display
-    */
+	/*
+	 * Bitplane calculations and check the Memory Requirements
+	 */
 
-static int amiga_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
-{
-   struct amiga_fb_par par;
-   int error = 0;
+	if (amifb_ilbm) {
+		par->next_plane = div8(upx(16<<maxfmode, par->vxres));
+		par->next_line = par->bpp*par->next_plane;
+		if (par->next_line * par->vyres > videomemorysize)
+			return -EINVAL;
+	} else {
+		par->next_line = div8(upx(16<<maxfmode, par->vxres));
+		par->next_plane = par->vyres*par->next_line;
+		if (par->next_plane * par->bpp > videomemorysize)
+			return -EINVAL;
+	}
 
-   if (con == -1)
-      amiga_fb_get_par(&par);
-   else
-      error = fbhw->decode_var(&disp[con].var, &par);
-   return(error ? error : fbhw->encode_fix(fix, &par));
-}
+	/*
+	 * Hardware Register Values
+	 */
 
+	par->bplcon0 = BPC0_COLOR | bplpixmode[clk_shift];
+	if (!IS_OCS)
+		par->bplcon0 |= BPC0_ECSENA;
+	if (par->bpp == 8)
+		par->bplcon0 |= BPC0_BPU3;
+	else
+		par->bplcon0 |= par->bpp<<12;
+	if (var->nonstd == FB_NONSTD_HAM)
+		par->bplcon0 |= BPC0_HAM;
+	if (var->sync & FB_SYNC_EXT)
+		par->bplcon0 |= BPC0_ERSY;
+
+	if (IS_AGA)
+		par->fmode = bplfetchmode[maxfmode];
+
+	switch (par->vmode & FB_VMODE_MASK) {
+		case FB_VMODE_INTERLACED:
+			par->bplcon0 |= BPC0_LACE;
+			break;
+		case FB_VMODE_DOUBLE:
+			if (IS_AGA)
+				par->fmode |= FMODE_SSCAN2 | FMODE_BSCAN2;
+			break;
+	}
+
+	if (!((par->vmode ^ var->vmode) & FB_VMODE_YWRAP)) {
+		par->xoffset = var->xoffset;
+		par->yoffset = var->yoffset;
+		if (par->vmode & FB_VMODE_YWRAP) {
+			if (par->xoffset || par->yoffset < 0 || par->yoffset >= par->vyres)
+				par->xoffset = par->yoffset = 0;
+		} else {
+			if (par->xoffset < 0 || par->xoffset > upx(16<<maxfmode, par->vxres-par->xres) ||
+			    par->yoffset < 0 || par->yoffset > par->vyres-par->yres)
+				par->xoffset = par->yoffset = 0;
+		}
+	} else
+		par->xoffset = par->yoffset = 0;
 
-   /*
-    *    Get the User Defined Part of the Display
-    */
+	par->crsr.crsr_x = par->crsr.crsr_y = 0;
+	par->crsr.spot_x = par->crsr.spot_y = 0;
+	par->crsr.height = par->crsr.width = 0;
 
-static int amiga_fb_get_var(struct fb_var_screeninfo *var, int con)
-{
-   struct amiga_fb_par par;
-   int error = 0;
+	if (hrate < hfmin || hrate > hfmax || vrate < vfmin || vrate > vfmax)
+		return -EINVAL;
 
-   if (con == -1) {
-      amiga_fb_get_par(&par);
-      error = fbhw->encode_var(var, &par);
-   } else
-      *var = disp[con].var;
-   return(error);
+	return 0;
 }
 
+	/*
+	 * Fill the `var' structure based on the values in `par' and maybe
+	 * other values read out of the hardware.
+	 */
 
-static void amiga_fb_set_disp(int con)
+static int ami_encode_var(struct fb_var_screeninfo *var,
+                          struct amiga_fb_par *par)
 {
-   struct fb_fix_screeninfo fix;
+	u_short clk_shift, line_shift;
+	int i;
 
-   amiga_fb_get_fix(&fix, con);
-   if (con == -1)
-      con = 0;
-   disp[con].screen_base = (u_char *)fix.smem_start;
-   disp[con].visual = fix.visual;
-   disp[con].type = fix.type;
-   disp[con].type_aux = fix.type_aux;
-   disp[con].ypanstep = fix.ypanstep;
-   disp[con].ywrapstep = fix.ywrapstep;
-   disp[con].line_length = fix.line_length;
-   disp[con].can_soft_blank = 1;
-   disp[con].inverse = amifb_inverse;
-}
+	clk_shift = par->clk_shift;
+	line_shift = par->line_shift;
 
+	var->xres = par->xres;
+	var->yres = par->yres;
+	var->xres_virtual = par->vxres;
+	var->yres_virtual = par->vyres;
+	var->xoffset = par->xoffset;
+	var->yoffset = par->yoffset;
 
-   /*
-    *    Set the User Defined Part of the Display
-    */
+	var->bits_per_pixel = par->bpp;
+	var->grayscale = 0;
 
-static int amiga_fb_set_var(struct fb_var_screeninfo *var, int con)
-{
-   int err, oldxres, oldyres, oldvxres, oldvyres, oldbpp;
+	if (IS_AGA) {
+		var->red.offset = 0;
+		var->red.length = 8;
+		var->red.msb_right = 0;
+	} else {
+		if (clk_shift == TAG_SHRES) {
+			var->red.offset = 0;
+			var->red.length = 2;
+			var->red.msb_right = 0;
+		} else {
+			var->red.offset = 0;
+			var->red.length = 4;
+			var->red.msb_right = 0;
+		}
+	}
+	var->blue = var->green = var->red;
+	var->transp.offset = 0;
+	var->transp.length = 0;
+	var->transp.msb_right = 0;
 
-   if ((err = do_fb_set_var(var, con == currcon)))
-      return(err);
-   if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
-      oldxres = disp[con].var.xres;
-      oldyres = disp[con].var.yres;
-      oldvxres = disp[con].var.xres_virtual;
-      oldvyres = disp[con].var.yres_virtual;
-      oldbpp = disp[con].var.bits_per_pixel;
-      disp[con].var = *var;
-      if (oldxres != var->xres || oldyres != var->yres ||
-          oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
-          oldbpp != var->bits_per_pixel) {
-         amiga_fb_set_disp(con);
-         (*fb_info.changevar)(con);
-         alloc_cmap(&disp[con].cmap, 0, 0);
-         do_install_cmap(con);
-      }
-   }
-   var->activate = 0;
-   return(0);
-}
+	if (par->bplcon0 & BPC0_HAM)
+		var->nonstd = FB_NONSTD_HAM;
+	else
+		var->nonstd = 0;
+	var->activate = 0;
 
+	var->height = -1;
+	var->width = -1;
+	var->accel = 0;
 
-   /*
-    *    Get the Colormap
-    */
+	var->pixclock = pixclock[clk_shift];
 
-static int amiga_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
-{
-   if (con == currcon) /* current console? */
-      return(do_fb_get_cmap(cmap, &disp[con].var, kspc));
-   else if (disp[con].cmap.len) /* non default colormap? */
-      copy_cmap(&disp[con].cmap, cmap, kspc ? 0 : 2);
-   else
-      copy_cmap(get_default_colormap(disp[con].var.bits_per_pixel), cmap,
-                kspc ? 0 : 2);
-   return(0);
-}
+	if (IS_AGA && par->fmode & FMODE_BSCAN2)
+		var->vmode = FB_VMODE_DOUBLE;
+	else if (par->bplcon0 & BPC0_LACE)
+		var->vmode = FB_VMODE_INTERLACED;
+	else
+		var->vmode = FB_VMODE_NONINTERLACED;
 
+	if (!IS_OCS && par->beamcon0 & BMC0_VARBEAMEN) {
+		var->hsync_len = (par->hsstop-par->hsstrt)>>clk_shift;
+		var->right_margin = par->hsstrt>>clk_shift;
+		var->left_margin = (par->htotal>>clk_shift) - var->xres - var->right_margin - var->hsync_len;
+		var->vsync_len = (par->vsstop-par->vsstrt)>>line_shift;
+		var->lower_margin = par->vsstrt>>line_shift;
+		var->upper_margin = (par->vtotal>>line_shift) - var->yres - var->lower_margin - var->vsync_len;
+		var->sync = 0;
+		if (par->beamcon0 & BMC0_HSYTRUE)
+			var->sync |= FB_SYNC_HOR_HIGH_ACT;
+		if (par->beamcon0 & BMC0_VSYTRUE)
+			var->sync |= FB_SYNC_VERT_HIGH_ACT;
+		if (par->beamcon0 & BMC0_CSYTRUE)
+			var->sync |= FB_SYNC_COMP_HIGH_ACT;
+	} else {
+		var->sync = FB_SYNC_BROADCAST;
+		var->hsync_len = (152>>clk_shift) + mod4(par->diwstop_h);
+		var->right_margin = ((par->htotal - down4(par->diwstop_h))>>clk_shift) + var->hsync_len;
+		var->left_margin = (par->htotal>>clk_shift) - var->xres - var->right_margin - var->hsync_len;
+		var->vsync_len = 4>>line_shift;
+		var->lower_margin = ((par->vtotal - par->diwstop_v)>>line_shift) + var->vsync_len;
+		var->upper_margin = (((par->vtotal - 2)>>line_shift) + 1) - var->yres -
+		                    var->lower_margin - var->vsync_len;
+	}
 
-   /*
-    *    Set the Colormap
-    */
+	if (par->bplcon0 & BPC0_ERSY)
+		var->sync |= FB_SYNC_EXT;
+	if (par->vmode & FB_VMODE_YWRAP)
+		var->vmode |= FB_VMODE_YWRAP;
 
-static int amiga_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
-{
-   int err;
+	for (i = 0; i < arraysize(var->reserved); i++)
+		var->reserved[i] = 0;
 
-   if (!disp[con].cmap.len) {       /* no colormap allocated? */
-      if ((err = alloc_cmap(&disp[con].cmap, 1<<disp[con].var.bits_per_pixel,
-                            0)))
-         return(err);
-   }
-   if (con == currcon)              /* current console? */
-      return(do_fb_set_cmap(cmap, &disp[con].var, kspc));
-   else
-      copy_cmap(cmap, &disp[con].cmap, kspc ? 0 : 1);
-   return(0);
+	return 0;
 }
 
+	/*
+	 * Get current hardware setting
+	 */
 
-   /*
-    *    Pan or Wrap the Display
-    *
-    *    This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
-    */
-
-static int amiga_fb_pan_display(struct fb_var_screeninfo *var, int con)
+static void ami_get_par(struct amiga_fb_par *par)
 {
-   int err;
-   u_short oldlatch;
-
-   if (var->vmode & FB_VMODE_YWRAP) {
-      if (var->xoffset || var->yoffset >= disp[con].var.yres)
-         return(-EINVAL);
-   } else {
-      if (var->xoffset+disp[con].var.xres > disp[con].var.xres_virtual ||
-          var->yoffset+disp[con].var.yres > disp[con].var.yres_virtual)
-    return(-EINVAL);
-   }
-   if (con == currcon) {
-      cli();
-      oldlatch = do_vmode;
-      do_vmode = 0;
-      sti();
-      if ((err = fbhw->pan_display(var, &current_par))) {
-         if (oldlatch)
-            do_vmode = 1;
-         return(err);
-      }
-      do_vmode = 1;
-   }
-   disp[con].var.xoffset = var->xoffset;
-   disp[con].var.yoffset = var->yoffset;
-   if (var->vmode & FB_VMODE_YWRAP)
-      disp[con].var.vmode |= FB_VMODE_YWRAP;
-   else
-      disp[con].var.vmode &= ~FB_VMODE_YWRAP;
-   return(0);
+	*par = currentpar;
 }
 
+	/*
+	 * Set new videomode
+	 */
 
-   /*
-    *    Amiga Frame Buffer Specific ioctls
-    */
-
-static int amiga_fb_ioctl(struct inode *inode, struct file *file,
-                          u_int cmd, u_long arg, int con)
+static void ami_set_var(struct fb_var_screeninfo *var)
 {
-   int i;
-   struct fb_fix_cursorinfo crsrfix;
-   struct fb_var_cursorinfo crsrvar;
-   struct fb_cursorstate crsrstate;
-
-   switch (cmd) {
-#ifdef CONFIG_AMIFB_AGA
-      case FBIOGET_FCURSORINFO:
-         i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrfix));
-         if (i)
-            return(i);
-         i = amiga_fb_get_fix_cursorinfo(&crsrfix, con);
-         memcpy_tofs((void *)arg, &crsrfix, sizeof(crsrfix));
-         return(i);
-      case FBIOGET_VCURSORINFO:
-         i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrvar));
-         if (i)
-            return(i);
-         i = amiga_fb_get_var_cursorinfo(&crsrvar, con);
-         memcpy_tofs((void *)arg, &crsrvar, sizeof(crsrvar));
-         return(i);
-      case FBIOPUT_VCURSORINFO:
-         i = verify_area(VERIFY_READ, (void *)arg, sizeof(crsrvar));
-         if (i)
-            return(i);
-         memcpy_fromfs(&crsrvar, (void *)arg, sizeof(crsrvar));
-         i = amiga_fb_set_var_cursorinfo(&crsrvar, con);
-         return(i);
-      case FBIOGET_CURSORSTATE:
-         i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(crsrstate));
-         if (i)
-            return(i);
-         i = amiga_fb_get_cursorstate(&crsrstate, con);
-         memcpy_tofs((void *)arg, &crsrstate, sizeof(crsrstate));
-         return(i);
-      case FBIOPUT_CURSORSTATE:
-         i = verify_area(VERIFY_READ, (void *)arg, sizeof(crsrstate));
-         if (i)
-            return(i);
-         memcpy_fromfs(&crsrstate, (void *)arg, sizeof(crsrstate));
-         i = amiga_fb_set_cursorstate(&crsrstate, con);
-         return(i);
-#endif /* CONFIG_AMIFB_AGA */
-#if 1
-      case FBCMD_GET_CURRENTPAR:
-         if ((i = verify_area(VERIFY_WRITE, (void *)arg,
-                              sizeof(struct amiga_fb_par))))
-            return(i);
-         memcpy_tofs((void *)arg, (void *)&current_par,
-                     sizeof(struct amiga_fb_par));
-         return(0);
-         break;
-      case FBCMD_SET_CURRENTPAR:
-         if ((i = verify_area(VERIFY_READ, (void *)arg,
-                              sizeof(struct amiga_fb_par))))
-            return(i);
-         memcpy_fromfs((void *)&current_par, (void *)arg,
-                       sizeof(struct amiga_fb_par));
-         return(0);
-         break;
-#endif
-   }
-   return(-EINVAL);
+	do_vmode_pan = 0;
+	do_vmode_full = 0;
+	ami_decode_var(var, &currentpar);
+	ami_build_copper();
+	do_vmode_full = 1;
+}
+
+#ifdef DEBUG
+static void ami_set_par(struct amiga_fb_par *par)
+{
+	do_vmode_pan = 0;
+	do_vmode_full = 0;
+	currentpar = *par;
+	ami_build_copper();
+	do_vmode_full = 1;
 }
+#endif
 
+	/*
+	 * Pan or Wrap the Display
+	 *
+	 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+	 * in `var'.
+	 */
 
-#ifdef CONFIG_AMIFB_AGA
-   /*
-    *    Hardware Cursor
-    */
-
-static int amiga_fb_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con)
+static void ami_pan_var(struct fb_var_screeninfo *var)
 {
-   if (boot_info.bi_amiga.chipset == CS_AGA)
-      return(aga_get_fix_cursorinfo(fix, con));
-   return(-EINVAL);
-}
+	struct amiga_fb_par *par = &currentpar;
 
+	par->xoffset = var->xoffset;
+	par->yoffset = var->yoffset;
+	if (var->vmode & FB_VMODE_YWRAP)
+		par->vmode |= FB_VMODE_YWRAP;
+	else
+		par->vmode &= ~FB_VMODE_YWRAP;
 
-static int amiga_fb_get_var_cursorinfo(struct fb_var_cursorinfo *var, int con)
-{
-   if (boot_info.bi_amiga.chipset == CS_AGA)
-      return(aga_get_var_cursorinfo(var, con));
-   return(-EINVAL);
+	do_vmode_pan = 0;
+	ami_update_par();
+	do_vmode_pan = 1;
 }
 
+	/*
+	 * Update hardware
+	 */
 
-static int amiga_fb_set_var_cursorinfo(struct fb_var_cursorinfo *var, int con)
+static int ami_update_par(void)
 {
-   if (boot_info.bi_amiga.chipset == CS_AGA)
-      return(aga_set_var_cursorinfo(var, con));
-   return(-EINVAL);
-}
-
+	struct amiga_fb_par *par = &currentpar;
+	short clk_shift, vshift, fstrt, fsize, fstop, fconst,  shift, move, mod;
 
-static int amiga_fb_get_cursorstate(struct fb_cursorstate *state, int con)
-{
-   if (boot_info.bi_amiga.chipset == CS_AGA)
-      return(aga_get_cursorstate(state, con));
-   return(-EINVAL);
-}
+	clk_shift = par->clk_shift;
 
+	if (!(par->vmode & FB_VMODE_SMOOTH_XPAN))
+		par->xoffset = upx(16<<maxfmode, par->xoffset);
+
+	fconst = 16<<maxfmode<<clk_shift;
+	vshift = modx(16<<maxfmode, par->xoffset);
+	fstrt = par->diwstrt_h - (vshift<<clk_shift) - 4;
+	fsize = (par->xres+vshift)<<clk_shift;
+	shift = modx(fconst, fstrt);
+	move = downx(2<<maxfmode, div8(par->xoffset));
+	if (maxfmode + clk_shift > 1) {
+		fstrt = downx(fconst, fstrt) - 64;
+		fsize = upx(fconst, fsize);
+		fstop = fstrt + fsize - fconst;
+	} else {
+		mod = fstrt = downx(fconst, fstrt) - fconst;
+		fstop = fstrt + upx(fconst, fsize) - 64;
+		fsize = up64(fsize);
+		fstrt = fstop - fsize + 64;
+		if (fstrt < min_fstrt) {
+			fstop += min_fstrt - fstrt;
+			fstrt = min_fstrt;
+		}
+		move = move - div8((mod-fstrt)>>clk_shift);
+	}
+	mod = par->next_line - div8(fsize>>clk_shift);
+	par->ddfstrt = fstrt;
+	par->ddfstop = fstop;
+	par->bplcon1 = hscroll2hw(shift);
+	par->bpl2mod = mod;
+	if (par->bplcon0 & BPC0_LACE)
+		par->bpl2mod += par->next_line;
+	if (IS_AGA && (par->fmode & FMODE_BSCAN2))
+		par->bpl1mod = -div8(fsize>>clk_shift);
+	else
+		par->bpl1mod = par->bpl2mod;
 
-static int amiga_fb_set_cursorstate(struct fb_cursorstate *state, int con)
-{
-   if (boot_info.bi_amiga.chipset == CS_AGA)
-      return(aga_set_cursorstate(state, con));
-   return(-EINVAL);
-}
-#endif /* CONFIG_AMIFB_AGA */
+	if (par->yoffset) {
+		par->bplpt0 = ZTWO_PADDR((u_long)videomemory + par->next_line*par->yoffset + move);
+		if (par->vmode & FB_VMODE_YWRAP) {
+			if (par->yoffset > par->vyres-par->yres) {
+				par->bplpt0wrap = ZTWO_PADDR((u_long)videomemory + move);
+				if (par->bplcon0 & BPC0_LACE && mod2(par->diwstrt_v+par->vyres-par->yoffset))
+					par->bplpt0wrap += par->next_line;
+			}
+		}
+	} else
+		par->bplpt0 = ZTWO_PADDR((u_long)videomemory + move);
 
+	if (par->bplcon0 & BPC0_LACE && mod2(par->diwstrt_v))
+		par->bplpt0 += par->next_line;
 
-static struct fb_ops amiga_fb_ops = {
-   amiga_fb_get_fix, amiga_fb_get_var, amiga_fb_set_var, amiga_fb_get_cmap,
-   amiga_fb_set_cmap, amiga_fb_pan_display, amiga_fb_ioctl
-};
+	return 0;
+}
 
+	/*
+	 * Read a single color register and split it into
+	 * colors/transparent. Return != 0 for invalid regno.
+	 */
 
-void amiga_video_setup(char *options, int *ints)
+static int ami_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+                         u_int *transp)
 {
-   char *this_opt;
-   int i;
-   char mcap_spec[80];
-
-   /*
-    *    Check for a Graphics Board
-    */
-
-#ifdef CONFIG_FB_CYBER
-   if (options && *options)
-      if (!strncmp(options, "cyber", 5) && Cyber_probe()) {
-         amifb_Cyber = 1;
-         Cyber_video_setup(options, ints);
-         return;
-      }
-#endif /* CONFIG_FB_CYBER */
+	if (IS_AGA) {
+		if (regno > 255)
+			return 1;
+	} else {
+		if (regno > 31)
+			return 1;
+	}
 
-#ifdef USE_MONO_AMIFB_IF_NON_AGA
-   if (boot_info.bi_amiga.chipset != CS_AGA) {
-      mono_video_setup(options, ints);
-      return;
-   }
-#endif /* USE_MONO_AMIFB_IF_NON_AGA */
-
-   mcap_spec[0] = '\0';
-   fb_info.fontname[0] = '\0';
-
-   if (!options || !*options)
-      return;
-
-   for (this_opt = strtok(options, ","); this_opt; this_opt = strtok(NULL, ","))
-      if (!strcmp(this_opt, "inverse")) {
-         amifb_inverse = 1;
-         for (i = 0; i < 16; i++) {
-            red16[i] = ~red16[i];
-            green16[i] = ~green16[i];
-            blue16[i] = ~blue16[i];
-         }
-         for (i = 0; i < 8; i++) {
-            red8[i] = ~red8[i];
-            green8[i] = ~green8[i];
-            blue8[i] = ~blue8[i];
-         }
-         for (i = 0; i < 4; i++) {
-            red4[i] = ~red4[i];
-            green4[i] = ~green4[i];
-            blue4[i] = ~blue4[i];
-         }
-         for (i = 0; i < 2; i++) {
-            red2[i] = ~red2[i];
-            green2[i] = ~green2[i];
-            blue2[i] = ~blue2[i];
-         }
-      } else if (!strcmp(this_opt, "ilbm"))
-         amifb_ilbm = 1;
-      else if (!strcmp(this_opt, "pwrsave"))
-         pwrsave = 1;
-      else if (!strncmp(this_opt, "monitorcap:", 11))
-         strcpy(mcap_spec, this_opt+11);
-      else if (!strncmp(this_opt, "font:", 5))
-         strcpy(fb_info.fontname, this_opt+5);
-      else
-         amifb_mode = get_video_mode(this_opt);
-
-   if (*mcap_spec) {
-      char *p;
-      int vmin, vmax, hmin, hmax;
-
-      /* Format for monitor capabilities is: <Vmin>;<Vmax>;<Hmin>;<Hmax>
-       * <V*> vertical freq. in Hz
-       * <H*> horizontal freq. in kHz
-       */
-
-      if (!(p = strtoke(mcap_spec, ";")) || !*p)
-         goto cap_invalid;
-      vmin = simple_strtoul(p, NULL, 10);
-      if (vmin <= 0)
-         goto cap_invalid;
-      if (!(p = strtoke(NULL, ";")) || !*p)
-         goto cap_invalid;
-      vmax = simple_strtoul(p, NULL, 10);
-      if (vmax <= 0 || vmax <= vmin)
-         goto cap_invalid;
-      if (!(p = strtoke(NULL, ";")) || !*p)
-         goto cap_invalid;
-      hmin = 1000 * simple_strtoul(p, NULL, 10);
-      if (hmin <= 0)
-         goto cap_invalid;
-      if (!(p = strtoke(NULL, "")) || !*p)
-         goto cap_invalid;
-      hmax = 1000 * simple_strtoul(p, NULL, 10);
-      if (hmax <= 0 || hmax <= hmin)
-         goto cap_invalid;
-
-      vfmin = vmin;
-      vfmax = vmax;
-      hfmin = hmin;
-      hfmax = hmax;
-cap_invalid:
-      ;
-   }
+	*red = palette[regno].red;
+	*green = palette[regno].green;
+	*blue = palette[regno].blue;
+	return 0;
 }
 
 
-   /*
-    *    Initialization
-    */
+	/*
+	 * 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.
+	 */
 
-struct fb_info *amiga_fb_init(long *mem_start)
+static int ami_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                         u_int transp)
 {
-   int err, tag, i;
-   struct fb_var_screeninfo *var;
-
-   /*
-    *    Check for a Graphics Board
-    */
-
-#ifdef CONFIG_FB_CYBER
-   if (amifb_Cyber)
-      return(Cyber_fb_init(mem_start));
-#endif /* CONFIG_FB_CYBER */
+#if defined(CONFIG_AMIFB_AGA)
+	u_short bplcon3 = currentpar.bplcon3;
 
-   /*
-    *    Use the Builtin Chipset
-    */
-
-   if (!AMIGAHW_PRESENT(AMI_VIDEO))
-      return(NULL);
-
-#ifdef USE_MONO_AMIFB_IF_NON_AGA
-   if (boot_info.bi_amiga.chipset != CS_AGA)
-      return(mono_amiga_fb_init(mem_start));
-#endif /* USE_MONO_AMIFB_IF_NON_AGA */
+	if (IS_AGA) {
+		if (regno > 255)
+			return 1;
+	} else
+#endif
+		if (regno > 31)
+			return 1;
 
-   switch (boot_info.bi_amiga.chipset) {
-#ifdef CONFIG_AMIFB_OCS
-      case CS_OCS:
-         strcat(amiga_fb_name, "OCS");
-default_chipset:
-         fbhw = &ocs_switch;
-         maxdepth[TAG_SHRES-1] = 0;       /* OCS means no SHRES */
-         maxdepth[TAG_HIRES-1] = 4;
-         maxdepth[TAG_LORES-1] = 6;
-         break;
-#endif /* CONFIG_AMIFB_OCS */
+	/*
+	 * Update the corresponding Hardware Color Register, unless it's Color
+	 * Register 0 and the screen is blanked.
+	 *
+	 * VBlank is switched off to protect bplcon3 or ecs_palette[] from
+	 * being changed by ami_do_blank() during the VBlank.
+	 */
 
-#ifdef CONFIG_AMIFB_ECS
-      case CS_ECS:
-         strcat(amiga_fb_name, "ECS");
-         fbhw = &ecs_switch;
-         maxdepth[TAG_SHRES-1] = 2;
-         maxdepth[TAG_HIRES-1] = 4;
-         maxdepth[TAG_LORES-1] = 6;
-         break;
-#endif /* CONFIG_AMIFB_ECS */
+	palette[regno].red = red;
+	palette[regno].green = green;
+	palette[regno].blue = blue;
 
-#ifdef CONFIG_AMIFB_AGA
-      case CS_AGA:
-         strcat(amiga_fb_name, "AGA");
-         fbhw = &aga_switch;
-         maxdepth[TAG_SHRES-1] = 8;
-         maxdepth[TAG_HIRES-1] = 8;
-         maxdepth[TAG_LORES-1] = 8;
-         break;
-#endif /* CONFIG_AMIFB_AGA */
+	if (regno || !is_blanked) {
+#if defined(CONFIG_AMIFB_AGA)
+		if (IS_AGA) {
+			VBlankOff();
+			custom.bplcon3 = bplcon3 | (regno<<8 & 0xe000);
+			custom.color[regno&31] = rgb2hw8_high(red, green, blue);
+			custom.bplcon3 = bplcon3 | (regno<<8 & 0xe000) | BPC3_LOCT;
+			custom.color[regno&31] = rgb2hw8_low(red, green, blue);
+			custom.bplcon3 = bplcon3;
+			VBlankOn();
+		} else
+#endif
+		{
+#if defined(CONFIG_AMIFB_ECS)
+			if (currentpar.bplcon0 & BPC0_SHRES) {
+				u_short color, mask;
+				int i;
+
+				mask = 0x3333;
+				color = rgb2hw2(red, green, blue);
+				VBlankOff();
+				for (i = regno+12; i >= (int)regno; i -= 4)
+					custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
+				mask <<=2; color >>= 2;
+				regno = down16(regno)+mul4(mod4(regno));
+				for (i = regno+3; i >= (int)regno; i--)
+					custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
+				VBlankOn();
+			} else
+#endif
+				custom.color[regno] = rgb2hw4(red, green, blue);
+		}
+	}
+	return 0;
+}
 
-      default:
-#ifdef CONFIG_AMIFB_OCS
-         printk("Unknown graphics chipset, defaulting to OCS\n");
-         strcat(amiga_fb_name, "Unknown");
-         goto default_chipset;
-#else /* CONFIG_AMIFB_OCS */
-         panic("Unknown graphics chipset, no default driver");
-#endif /* CONFIG_AMIFB_OCS */
-         break;
-   }
+static void ami_update_display(void)
+{
+	struct amiga_fb_par *par = &currentpar;
 
-   /*
-    *    Calculate the Pixel Clock Values for this Machine
-    */
+	custom.bplcon1 = par->bplcon1;
+	custom.bpl1mod = par->bpl1mod;
+	custom.bpl2mod = par->bpl2mod;
+	custom.ddfstrt = ddfstrt2hw(par->ddfstrt);
+	custom.ddfstop = ddfstop2hw(par->ddfstop);
+}
 
-   __asm("movel %3,%%d0;"
-         "movel #0x00000005,%%d1;"     /*  25E9: SHRES:  35 ns / 28 MHz */
-         "movel #0xd21dba00,%%d2;"
-         "divul %%d0,%%d1,%%d2;"
-         "movel %%d2,%0;"
-         "movel #0x0000000b,%%d1;"     /*  50E9: HIRES:  70 ns / 14 MHz */
-         "movel #0xa43b7400,%%d2;"
-         "divul %%d0,%%d1,%%d2;"
-         "movel %%d2,%1;"
-         "movel #0x00000017,%%d1;"     /* 100E9: LORES: 140 ns /  7 MHz */
-         "movel #0x4876e800,%%d2;"
-         "divul %%d0,%%d1,%%d2;"
-         "movel %%d2,%2"
-         : "=r" (pixclock[TAG_SHRES-1]), "=r" (pixclock[TAG_HIRES-1]),
-           "=r" (pixclock[TAG_LORES-1])
-         : "r" (amiga_eclock)
-         : "%%d0", "%%d1", "%%d2");
+	/*
+	 * Change the video mode (called by VBlank interrupt)
+	 */
 
-   /*
-    *    Replace the Tag Values with the Real Pixel Clock Values
-    */
+static void ami_init_display(void)
+{
+	struct amiga_fb_par *par = &currentpar;
 
-   for (i = 0; i < NUM_PREDEF_MODES; i++) {
-      tag = amiga_fb_predefined[i].pixclock;
-      if (tag == TAG_SHRES || tag == TAG_HIRES || tag == TAG_LORES) {
-         amiga_fb_predefined[i].pixclock = pixclock[tag-1];
-         if (amiga_fb_predefined[i].bits_per_pixel > maxdepth[tag-1])
-            amiga_fb_predefined[i].bits_per_pixel = maxdepth[tag-1];
-      }
-   }
+	custom.bplcon0 = par->bplcon0 & ~BPC0_LACE;
+	custom.bplcon2 = (IS_OCS ? 0 : BPC2_KILLEHB) | BPC2_PF2P2 | BPC2_PF1P2;
+	if (!IS_OCS) {
+		custom.bplcon3 = par->bplcon3;
+		if (IS_AGA)
+			custom.bplcon4 = BPC4_ESPRM4 | BPC4_OSPRM4;
+		if (par->beamcon0 & BMC0_VARBEAMEN) {
+			custom.htotal = htotal2hw(par->htotal);
+			custom.hbstrt = hbstrt2hw(par->hbstrt);
+			custom.hbstop = hbstop2hw(par->hbstop);
+			custom.hsstrt = hsstrt2hw(par->hsstrt);
+			custom.hsstop = hsstop2hw(par->hsstop);
+			custom.hcenter = hcenter2hw(par->hcenter);
+			custom.vtotal = vtotal2hw(par->vtotal);
+			custom.vbstrt = vbstrt2hw(par->vbstrt);
+			custom.vbstop = vbstop2hw(par->vbstop);
+			custom.vsstrt = vsstrt2hw(par->vsstrt);
+			custom.vsstop = vsstop2hw(par->vsstop);
+		}
+	}
+	if (!IS_OCS || par->hsstop)
+		custom.beamcon0 = par->beamcon0;
+	if (IS_AGA)
+		custom.fmode = par->fmode;
 
-   err = register_framebuffer(amiga_fb_name, &node, &amiga_fb_ops,
-                              NUM_TOTAL_MODES, amiga_fb_predefined);
-   if (err < 0)
-      panic("Cannot register frame buffer");
+	/*
+	 * The minimum period for audio depends on htotal
+	 */
 
-   fbhw->init();
-   check_default_mode();
+	amiga_audio_min_period = div16(par->htotal);
 
-   if (!add_isr(IRQ_AMIGA_VERTB, amifb_interrupt, 0, NULL, "frame buffer"))
-      panic("Couldn't add vblank interrupt");
+	is_lace = par->bplcon0 & BPC0_LACE ? 1 : 0;
+#if 1
+	if (is_lace) {
+		if (custom.vposr & 0x8000)
+			custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][1]);
+		else
+			custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][0]);
+	} else {
+		custom.vposw = custom.vposr | 0x8000;
+		custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][1]);
+	}
+#else
+	custom.vposw = custom.vposr | 0x8000;
+	custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][1]);
+#endif
+}
 
-   strcpy(fb_info.modename, amiga_fb_name);
-   fb_info.disp = disp;
-   fb_info.switch_con = &amifb_switch;
-   fb_info.updatevar = &amifb_updatevar;
-   fb_info.blank = &amifb_blank;
+	/*
+	 * (Un)Blank the screen (called by VBlank interrupt)
+	 */
 
-   var = &amiga_fb_predefined[amifb_mode];
-   do_fb_set_var(var, 1);
-   strcat(fb_info.modename, " ");
-   strcat(fb_info.modename, amiga_fb_modenames[amifb_mode]);
+static void ami_do_blank(void)
+{
+	struct amiga_fb_par *par = &currentpar;
+#if defined(CONFIG_AMIFB_AGA)
+	u_short bplcon3 = par->bplcon3;
+#endif
+	u_char red, green, blue;
 
-   amiga_fb_get_var(&disp[0].var, -1);
-   amiga_fb_set_disp(-1);
-   do_install_cmap(0);
-   return(&fb_info);
+	if (do_blank > 0) {
+		custom.dmacon = DMAF_RASTER | DMAF_SPRITE;
+		red = green = blue = 0;
+		if (!IS_OCS && do_blank > 1) {
+			switch (do_blank) {
+				case 2 : /* suspend vsync */
+					custom.hsstrt = hsstrt2hw(par->hsstrt);
+					custom.hsstop = hsstop2hw(par->hsstop);
+					custom.vsstrt = vsstrt2hw(par->vtotal+4);
+					custom.vsstop = vsstop2hw(par->vtotal+4);
+					break;
+				case 3 : /* suspend hsync */
+					custom.hsstrt = hsstrt2hw(par->htotal+16);
+					custom.hsstop = hsstop2hw(par->htotal+16);
+					custom.vsstrt = vsstrt2hw(par->vsstrt);
+					custom.vsstop = vsstrt2hw(par->vsstop);
+					break;
+				case 4 : /* powerdown */
+					custom.hsstrt = hsstrt2hw(par->htotal+16);
+					custom.hsstop = hsstop2hw(par->htotal+16);
+					custom.vsstrt = vsstrt2hw(par->vtotal+4);
+					custom.vsstop = vsstop2hw(par->vtotal+4);
+					break;
+			}
+			if (!(par->beamcon0 & BMC0_VARBEAMEN)) {
+				custom.htotal = htotal2hw(par->htotal);
+				custom.vtotal = vtotal2hw(par->vtotal);
+				custom.beamcon0 = BMC0_HARDDIS | BMC0_VARBEAMEN |
+				                  BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARCSYEN;
+			}
+		}
+	} else {
+		custom.dmacon = DMAF_SETCLR | DMAF_RASTER | DMAF_SPRITE;
+		red = palette[0].red;
+		green = palette[0].green;
+		blue = palette[0].blue;
+		if (!IS_OCS) {
+			custom.hsstrt = hsstrt2hw(par->hsstrt);
+			custom.hsstop = hsstop2hw(par->hsstop);
+			custom.vsstrt = vsstrt2hw(par->vsstrt);
+			custom.vsstop = vsstop2hw(par->vsstop);
+			custom.beamcon0 = par->beamcon0;
+		}
+	}
+#if defined(CONFIG_AMIFB_AGA)
+	if (IS_AGA) {
+		custom.bplcon3 = bplcon3;
+		custom.color[0] = rgb2hw8_high(red, green, blue);
+		custom.bplcon3 = bplcon3 | BPC3_LOCT;
+		custom.color[0] = rgb2hw8_low(red, green, blue);
+		custom.bplcon3 = bplcon3;
+	} else
+#endif
+	{
+#if defined(CONFIG_AMIFB_ECS)
+		if (par->bplcon0 & BPC0_SHRES) {
+			u_short color, mask;
+			int i;
+
+			mask = 0x3333;
+			color = rgb2hw2(red, green, blue);
+			for (i = 12; i >= 0; i -= 4)
+				custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
+			mask <<=2; color >>= 2;
+			for (i = 3; i >= 0; i--)
+				custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
+		} else
+#endif
+			custom.color[0] = rgb2hw4(red, green, blue);
+	}
+	is_blanked = do_blank > 0 ? do_blank : 0;
 }
 
+	/*
+	 * Flash the cursor (called by VBlank interrupt)
+	 */
 
-static int amifb_switch(int con)
+static int ami_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix, int con)
 {
-   /* Do we have to save the colormap? */
-   if (disp[currcon].cmap.len)
-      do_fb_get_cmap(&disp[currcon].cmap, &disp[currcon].var, 1);
+	struct amiga_fb_par *par = &currentpar;
 
-   do_fb_set_var(&disp[con].var, 1);
-   currcon = con;
-   /* Install new colormap */
-   do_install_cmap(con);
-   return(0);
-}
+	fix->crsr_width = fix->crsr_xsize = par->crsr.width;
+	fix->crsr_height = fix->crsr_ysize = par->crsr.height;
+	fix->crsr_color1 = 17;
+	fix->crsr_color2 = 18;
+	return 0;
+}
+
+static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con)
+{
+	struct amiga_fb_par *par = &currentpar;
+	register u_short *lspr, *sspr;
+	register u_long datawords asm ("d2");
+	register short delta;
+	register u_char color;
+	short height, width, bits, words;
+	int i, size, alloc;
+
+	size = par->crsr.height*par->crsr.width;
+	alloc = var->height*var->width;
+	var->height = par->crsr.height;
+	var->width = par->crsr.width;
+	var->xspot = par->crsr.spot_x;
+	var->yspot = par->crsr.spot_y;
+	if (size > var->height*var->width)
+		return -ENAMETOOLONG;
+	if ((i = verify_area(VERIFY_WRITE, (void *)data, size)))
+		return i;
+	delta = 1<<par->crsr.fmode;
+	lspr = lofsprite + (delta<<1);
+	if (par->bplcon0 & BPC0_LACE)
+		sspr = shfsprite + (delta<<1);
+	else
+		sspr = 0;
+	for (height = (short)var->height-1; height >= 0; height--) {
+		bits = 0; words = delta; datawords = 0;
+		for (width = (short)var->width-1; width >= 0; width--) {
+			if (bits == 0) {
+				bits = 16; --words;
+				asm volatile ("movew %1@(%3:w:2),%0 ; swap %0 ; movew %1@+,%0"
+					: "=d" (datawords), "=a" (lspr) : "1" (lspr), "d" (delta));
+			}
+			--bits;
+			asm volatile (
+				"clrb %0 ; swap %1 ; lslw #1,%1 ; roxlb #1,%0 ; "
+				"swap %1 ; lslw #1,%1 ; roxlb #1,%0"
+				: "=d" (color), "=d" (datawords) : "1" (datawords));
+			put_fs_byte(color, data++);
+		}
+		if (bits > 0) {
+			--words; ++lspr;
+		}
+		while (--words >= 0)
+			++lspr;
+		asm volatile ("lea %0@(%4:w:2),%0 ; tstl %1 ; jeq 1f ; exg %0,%1\n1:"
+			: "=a" (lspr), "=a" (sspr) : "0" (lspr), "1" (sspr), "d" (delta));
+	}
+	return 0;
+}
+
+static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char *data, int con)
+{
+	struct amiga_fb_par *par = &currentpar;
+	register u_short *lspr, *sspr;
+	register u_long datawords asm ("d2");
+	register short delta;
+	u_short fmode;
+	short height, width, bits, words;
+	int i;
 
+	if (!var->width)
+		return -EINVAL;
+	else if (var->width <= 16)
+		fmode = TAG_FMODE_1;
+	else if (var->width <= 32)
+		fmode = TAG_FMODE_2;
+	else if (var->width <= 64)
+		fmode = TAG_FMODE_4;
+	else
+		return -EINVAL;
+	if (fmode > maxfmode)
+		return -EINVAL;
+	if (!var->height)
+		return -EINVAL;
+	if ((i = verify_area(VERIFY_READ, (void *)data, var->width*var->height)))
+		return i;
+	delta = 1<<fmode;
+	lofsprite = shfsprite = (u_short *)spritememory;
+	lspr = lofsprite + (delta<<1);
+	if (par->bplcon0 & BPC0_LACE) {
+		if (((var->height+4)<<fmode<<2) > SPRITEMEMSIZE)
+			return -EINVAL;
+		memset(lspr, 0, (var->height+4)<<fmode<<2);
+		shfsprite += ((var->height+5)&-2)<<fmode;
+		sspr = shfsprite + (delta<<1);
+	} else {
+		if (((var->height+2)<<fmode<<2) > SPRITEMEMSIZE)
+			return -EINVAL;
+		memset(lspr, 0, (var->height+2)<<fmode<<2);
+		sspr = 0;
+	}
+	for (height = (short)var->height-1; height >= 0; height--) {
+		bits = 16; words = delta; datawords = 0;
+		for (width = (short)var->width-1; width >= 0; width--) {
+			asm volatile (
+				"lsrb #1,%2 ; roxlw #1,%0 ; swap %0 ; "
+				"lsrb #1,%2 ; roxlw #1,%0 ; swap %0"
+				: "=d" (datawords) : "0" (datawords), "d" ((u_long)(get_fs_byte(data++))));
+			if (--bits == 0) {
+				bits = 16; --words;
+				asm volatile ("swap %2 ; movew %2,%0@(%3:w:2) ; swap %2 ; movew %2,%0@+"
+					: "=a" (lspr) : "0" (lspr), "d" (datawords), "d" (delta));
+			}
+		}
+		if (bits < 16) {
+			--words;
+			asm volatile (
+				"swap %2 ; lslw %4,%2 ; movew %2,%0@(%3:w:2) ; "
+				"swap %2 ; lslw %4,%2 ; movew %2,%0@+"
+				: "=a" (lspr) : "0" (lspr), "d" (datawords), "d" (delta), "d" (bits));
+		}
+		while (--words >= 0)
+			asm volatile ("moveql #0,%%d0 ; movew %%d0,%0@(%2:w:2) ; movew %%d0,%0@+"
+				: "=a" (lspr) : "0" (lspr), "d" (delta) : "d0");
+		asm volatile ("lea %0@(%4:w:2),%0 ; tstl %1 ; jeq 1f ; exg %0,%1\n1:"
+			: "=a" (lspr), "=a" (sspr) : "0" (lspr), "1" (sspr), "d" (delta));
+	}
+	par->crsr.height = var->height;
+	par->crsr.width = var->width;
+	par->crsr.spot_x = var->xspot;
+	par->crsr.spot_y = var->yspot;
+	par->crsr.fmode = fmode;
+	if (IS_AGA) {
+		par->fmode &= ~(FMODE_SPAGEM | FMODE_SPR32);
+		par->fmode |= sprfetchmode[fmode];
+		custom.fmode = par->fmode;
+	}
+	return 0;
+}
+
+static int ami_get_cursorstate(struct fb_cursorstate *state, int con)
+{
+	struct amiga_fb_par *par = &currentpar;
+
+	state->xoffset = par->crsr.crsr_x;
+	state->yoffset = par->crsr.crsr_y;
+	state->mode = cursormode;
+	return 0;
+}
+
+static int ami_set_cursorstate(struct fb_cursorstate *state, int con)
+{
+	struct amiga_fb_par *par = &currentpar;
+
+	par->crsr.crsr_x = state->xoffset;
+	par->crsr.crsr_y = state->yoffset;
+	if ((cursormode = state->mode) == FB_CURSOR_OFF)
+		cursorstate = -1;
+	do_cursor = 1;
+	return 0;
+}
+
+static void ami_set_sprite(void)
+{
+	struct amiga_fb_par *par = &currentpar;
+	copins *copl, *cops;
+	u_short hs, vs, ve;
+	u_long pl, ps, pt;
+	short mx, my;
+
+	cops = copdisplay.list[currentcop][0];
+	copl = copdisplay.list[currentcop][1];
+	ps = pl = ZTWO_PADDR(dummysprite);
+	mx = par->crsr.crsr_x-par->crsr.spot_x;
+	my = par->crsr.crsr_y-par->crsr.spot_y;
+	if (!(par->vmode & FB_VMODE_YWRAP)) {
+		mx -= par->xoffset;
+		my -= par->yoffset;
+	}
+	if (!is_blanked && cursorstate > 0 && par->crsr.height > 0 &&
+	    mx > -(short)par->crsr.width && mx < par->xres &&
+	    my > -(short)par->crsr.height && my < par->yres) {
+		pl = ZTWO_PADDR(lofsprite);
+		hs = par->diwstrt_h + (mx<<par->clk_shift) - 4;
+		vs = par->diwstrt_v + (my<<par->line_shift);
+		ve = vs + (par->crsr.height<<par->line_shift);
+		if (par->bplcon0 & BPC0_LACE) {
+			ps = ZTWO_PADDR(shfsprite);
+			lofsprite[0] = spr2hw_pos(vs, hs);
+			shfsprite[0] = spr2hw_pos(vs+1, hs);
+			if (mod2(vs)) {
+				lofsprite[1<<par->crsr.fmode] = spr2hw_ctl(vs, hs, ve);
+				shfsprite[1<<par->crsr.fmode] = spr2hw_ctl(vs+1, hs, ve+1);
+				pt = pl; pl = ps; ps = pt;
+			} else {
+				lofsprite[1<<par->crsr.fmode] = spr2hw_ctl(vs, hs, ve+1);
+				shfsprite[1<<par->crsr.fmode] = spr2hw_ctl(vs+1, hs, ve);
+			}
+		} else {
+			lofsprite[0] = spr2hw_pos(vs, hs) | (IS_AGA && (par->fmode & FMODE_BSCAN2) ? 0x80 : 0);
+			lofsprite[1<<par->crsr.fmode] = spr2hw_ctl(vs, hs, ve);
+		}
+	}
+	copl[cop_spr0ptrh].w[1] = highw(pl);
+	copl[cop_spr0ptrl].w[1] = loww(pl);
+	if (par->bplcon0 & BPC0_LACE) {
+		cops[cop_spr0ptrh].w[1] = highw(ps);
+		cops[cop_spr0ptrl].w[1] = loww(ps);
+	}
+}
 
-   /*
-    *    Update the `var' structure (called by fbcon.c)
-    *
-    *    This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'.
-    *    Since it's called by a kernel driver, no range checking is done.
-    */
+	/*
+	 * Initialise the Copper Initialisation List
+	 */
 
-static int amifb_updatevar(int con)
+static void ami_init_copper(void)
 {
-   do_vmode = 0;
-   current_par.yoffset = disp[con].var.yoffset;
-   current_par.vmode = disp[con].var.vmode;
-   current_par.bplpt0 = ZTWO_PADDR((u_long)videomemory+
-                                   current_par.yoffset*current_par.next_line);
-   do_vmode = 1;
-   return(0);
-}
+	copins *cop = copdisplay.init;
+	u_long p;
+	int i;
 
+	if (!IS_OCS) {
+		(cop++)->l = CMOVE(BPC0_COLOR | BPC0_SHRES | BPC0_ECSENA, bplcon0);
+		(cop++)->l = CMOVE(0x0181, diwstrt);
+		(cop++)->l = CMOVE(0x0281, diwstop);
+		(cop++)->l = CMOVE(0x0000, diwhigh);
+	} else
+		(cop++)->l = CMOVE(BPC0_COLOR, bplcon0);
+	p = ZTWO_PADDR(dummysprite);
+	for (i = 0; i < 8; i++) {
+		(cop++)->l = CMOVE(0, spr[i].pos);
+		(cop++)->l = CMOVE(highw(p), sprpt[i]);
+		(cop++)->l = CMOVE2(loww(p), sprpt[i]);
+	}
 
-   /*
-    *    Blank the display.
-    */
+	(cop++)->l = CMOVE(IF_SETCLR | IF_COPER, intreq);
+	copdisplay.wait = cop;
+	(cop++)->l = CEND;
+	(cop++)->l = CMOVE(0, copjmp2);
+	cop->l = CEND;
 
-static void amifb_blank(int blank)
-{
-   do_blank = blank ? 1 : -1;
+	custom.cop1lc = (u_short *)ZTWO_PADDR(copdisplay.init);
+	custom.copjmp1 = 0;
 }
 
+static void ami_reinit_copper(void)
+{
+	struct amiga_fb_par *par = &currentpar;
 
-   /*
-    *    VBlank Display Interrupt
-    */
+	copdisplay.init[cip_bplcon0].w[1] = ~(BPC0_BPU3 | BPC0_BPU2 | BPC0_BPU1 | BPC0_BPU0) & par->bplcon0;
+	copdisplay.wait->l = CWAIT(32, par->diwstrt_v-4);
+}
 
-static void amifb_interrupt(int irq, struct pt_regs *fp, void *dummy)
-{
-   static int is_laced = 0;
+	/*
+	 * Build the Copper List
+	 */
 
+static void ami_build_copper(void)
+{
+	struct amiga_fb_par *par = &currentpar;
+	copins *copl, *cops;
+	u_long p;
+
+	currentcop = 1 - currentcop;
+
+	copl = copdisplay.list[currentcop][1];
+
+	(copl++)->l = CWAIT(0, 10);
+	(copl++)->l = CMOVE(par->bplcon0, bplcon0);
+	(copl++)->l = CMOVE(0, sprpt[0]);
+	(copl++)->l = CMOVE2(0, sprpt[0]);
+
+	if (par->bplcon0 & BPC0_LACE) {
+		cops = copdisplay.list[currentcop][0];
+
+		(cops++)->l = CWAIT(0, 10);
+		(cops++)->l = CMOVE(par->bplcon0, bplcon0);
+		(cops++)->l = CMOVE(0, sprpt[0]);
+		(cops++)->l = CMOVE2(0, sprpt[0]);
+
+		(copl++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v+1), diwstrt);
+		(copl++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v+1), diwstop);
+		(cops++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v), diwstrt);
+		(cops++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v), diwstop);
+		if (!IS_OCS) {
+			(copl++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v+1,
+			                    par->diwstop_h, par->diwstop_v+1), diwhigh);
+			(cops++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v,
+			                    par->diwstop_h, par->diwstop_v), diwhigh);
+#if 0
+			if (par->beamcon0 & BMC0_VARBEAMEN) {
+				(copl++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
+				(copl++)->l = CMOVE(vbstrt2hw(par->vbstrt+1), vbstrt);
+				(copl++)->l = CMOVE(vbstop2hw(par->vbstop+1), vbstop);
+				(cops++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
+				(cops++)->l = CMOVE(vbstrt2hw(par->vbstrt), vbstrt);
+				(cops++)->l = CMOVE(vbstop2hw(par->vbstop), vbstop);
+			}
+#endif
+		}
+		p = ZTWO_PADDR(copdisplay.list[currentcop][0]);
+		(copl++)->l = CMOVE(highw(p), cop2lc);
+		(copl++)->l = CMOVE2(loww(p), cop2lc);
+		p = ZTWO_PADDR(copdisplay.list[currentcop][1]);
+		(cops++)->l = CMOVE(highw(p), cop2lc);
+		(cops++)->l = CMOVE2(loww(p), cop2lc);
+		copdisplay.rebuild[0] = cops;
+	} else {
+		(copl++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v), diwstrt);
+		(copl++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v), diwstop);
+		if (!IS_OCS) {
+			(copl++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v,
+			                    par->diwstop_h, par->diwstop_v), diwhigh);
 #if 0
-   /*
-    *    This test should be here, in case current_par isn't initialized yet
-    *
-    *    Fortunately only do_flashcursor() will be called in that case, and
-    *    currently that function doesn't use current_par. But this may change
-    *    in future...
-    */
-   if (!current_par_valid)
-      return;
+			if (par->beamcon0 & BMC0_VARBEAMEN) {
+				(copl++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
+				(copl++)->l = CMOVE(vbstrt2hw(par->vbstrt), vbstrt);
+				(copl++)->l = CMOVE(vbstop2hw(par->vbstop), vbstop);
+			}
 #endif
+		}
+	}
+	copdisplay.rebuild[1] = copl;
 
-   /*
-    *    If interlaced, only change the display on a long frame
-    */
-
-   if (!is_laced || custom.vposr & 0x8000) {
-      if (do_vmode) {
-         fbhw->do_vmode();
-         do_vmode = 0;
-         is_laced = (current_par.vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED;
-      }
-      if (do_movecursor) {
-         fbhw->do_movecursor();
-         do_movecursor = 0;
-      }
-   }
-   if (do_blank) {
-      fbhw->do_blank(do_blank > 0 ? 1 : 0);
-      do_blank = 0;
-   }
-   if (!is_blanked)
-      fbhw->do_flashcursor();
-}
-
-
-   /*
-    *    A strtok which returns empty strings, too
-    */
-
-static char * strtoke(char * s,const char * ct)
-{
-   char *sbegin, *send;
-   static char *ssave = NULL;
-  
-   sbegin  = s ? s : ssave;
-   if (!sbegin)
-      return(NULL);
-   if (*sbegin == '\0') {
-      ssave = NULL;
-      return(NULL);
-   }
-   send = strpbrk(sbegin, ct);
-   if (send && *send != '\0')
-      *send++ = '\0';
-   ssave = send;
-   return(sbegin);
-}
-
-
-   /*
-    *    Get a Video Modes
-    */
-
-static int get_video_mode(const char *name)
-{
-   int i;
-
-   for (i = 1; i < NUM_PREDEF_MODES; i++)
-      if (!strcmp(name, amiga_fb_modenames[i]))
-         return(i);
-   return(0);
-}
-
-
-   /*
-    *    Check the Default Video Mode
-    */
+	ami_update_par();
+	ami_rebuild_copper();
+}
 
-static void check_default_mode(void)
+	/*
+	 * Rebuild the Copper List
+	 *
+	 * We only change the things that are not static
+	 */
+
+static void ami_rebuild_copper(void)
 {
-   struct fb_var_screeninfo var;
+	struct amiga_fb_par *par = &currentpar;
+	copins *copl, *cops;
+	u_short line, h_end1, h_end2;
+	short i;
+	u_long p;
 
-   /* First check the user supplied or system default video mode */
-   if (amifb_mode) {
-      var = amiga_fb_predefined[amifb_mode];
-      var.activate = FB_ACTIVATE_TEST;
-      if (!do_fb_set_var(&var, 1))
-         goto found_video_mode;
-   }
-
-   /* Try some other modes... */
-   printk("Can't use default video mode. Probing video modes...\n");
-   for (amifb_mode = 1; amifb_mode < NUM_PREDEF_MODES; amifb_mode++) {
-      var = amiga_fb_predefined[amifb_mode];
-      var.activate = FB_ACTIVATE_TEST;
-      if (!do_fb_set_var(&var, 1))
-         goto found_video_mode;
-   }
-   panic("Can't find any usable video mode");
+	if (IS_AGA && maxfmode + par->clk_shift == 0)
+		h_end1 = par->diwstrt_h-64;
+	else
+		h_end1 = par->htotal-32;
+	h_end2 = par->ddfstop+64;
+
+	ami_set_sprite();
 
-found_video_mode:
-   amiga_fb_predefined[0] = var;
+	copl = copdisplay.rebuild[1];
+	p = par->bplpt0;
+	if (par->vmode & FB_VMODE_YWRAP) {
+		if ((par->vyres-par->yoffset) != 1 || !mod2(par->diwstrt_v)) {
+			if (par->yoffset > par->vyres-par->yres) {
+				for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
+					(copl++)->l = CMOVE(highw(p), bplpt[i]);
+					(copl++)->l = CMOVE2(loww(p), bplpt[i]);
+				}
+				line = par->diwstrt_v + ((par->vyres-par->yoffset)<<par->line_shift) - 1;
+				while (line >= 512) {
+					(copl++)->l = CWAIT(h_end1, 510);
+					line -= 512;
+				}
+				if (line >= 510 && IS_AGA && maxfmode + par->clk_shift == 0)
+					(copl++)->l = CWAIT(h_end1, line);
+				else
+					(copl++)->l = CWAIT(h_end2, line);
+				p = par->bplpt0wrap;
+			}
+		} else p = par->bplpt0wrap;
+	}
+	for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
+		(copl++)->l = CMOVE(highw(p), bplpt[i]);
+		(copl++)->l = CMOVE2(loww(p), bplpt[i]);
+	}
+	copl->l = CEND;
+
+	if (par->bplcon0 & BPC0_LACE) {
+		cops = copdisplay.rebuild[0];
+		p = par->bplpt0;
+		if (mod2(par->diwstrt_v))
+			p -= par->next_line;
+		else
+			p += par->next_line;
+		if (par->vmode & FB_VMODE_YWRAP) {
+			if ((par->vyres-par->yoffset) != 1 || mod2(par->diwstrt_v)) {
+				if (par->yoffset > par->vyres-par->yres+1) {
+					for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
+						(cops++)->l = CMOVE(highw(p), bplpt[i]);
+						(cops++)->l = CMOVE2(loww(p), bplpt[i]);
+					}
+					line = par->diwstrt_v + ((par->vyres-par->yoffset)<<par->line_shift) - 2;
+					while (line >= 512) {
+						(cops++)->l = CWAIT(h_end1, 510);
+						line -= 512;
+					}
+					if (line > 510 && IS_AGA && maxfmode + par->clk_shift == 0)
+						(cops++)->l = CWAIT(h_end1, line);
+					else
+						(cops++)->l = CWAIT(h_end2, line);
+					p = par->bplpt0wrap;
+					if (mod2(par->diwstrt_v+par->vyres-par->yoffset))
+						p -= par->next_line;
+					else
+						p += par->next_line;
+				}
+			} else p = par->bplpt0wrap - par->next_line;
+		}
+		for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
+			(cops++)->l = CMOVE(highw(p), bplpt[i]);
+			(cops++)->l = CMOVE2(loww(p), bplpt[i]);
+		}
+		cops->l = CEND;
+	}
 }
diff -urN pc-2.0.21/arch/m68k/amiga/amiints.c linux-2.0.21/arch/m68k/amiga/amiints.c
--- pc-2.0.21/arch/m68k/amiga/amiints.c	Mon May 20 06:54:25 1996
+++ linux-2.0.21/arch/m68k/amiga/amiints.c	Sat Sep 14 15:17:31 1996
@@ -1,15 +1,30 @@
 /*
- * amiints.c -- Amiga Linux interrupt handling code
+ * linux/arch/m68k/amiga/amiints.c -- Amiga Linux interrupt handling code
  *
  * 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.
  *
+ * 11/07/96: rewritten interrupt handling, irq lists are exists now only for
+ *           this sources where it makes sense (VERTB/PORTS/EXTER) and you must
+ *           be careful that dev_id for this sources is unique since this the
+ *           only possibility to distinguish between different handlers for
+ *           free_irq. irq lists also have different irq flags:
+ *           - IRQ_FLG_FAST: handler is inserted at top of list (after other
+ *                           fast handlers)
+ *           - IRQ_FLG_SLOW: handler is inserted at bottom of list and before
+ *                           they're executed irq level is set to the previous
+ *                           one, but handlers don't need to be reentrant, if
+ *                           reentrance occured, slow handlers will be just
+ *                           called again.
+ *           The whole interrupt handling for CIAs is moved to cia.c
+ *           /Roman Zippel
  */
 
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/kernel_stat.h>
 
 #include <asm/system.h>
 #include <asm/irq.h>
@@ -17,32 +32,33 @@
 #include <asm/amigahw.h>
 #include <asm/amigaints.h>
 
-/* isr node variables for amiga interrupt sources */
-static isr_node_t *ami_lists[NUM_AMIGA_SOURCES];
+extern int cia_request_irq(struct ciabase *base,int irq,
+                           void (*handler)(int, void *, struct pt_regs *),
+                           unsigned long flags, const char *devname, void *dev_id);
+extern void cia_free_irq(struct ciabase *base, unsigned int irq, void *dev_id);
+extern void cia_init_IRQ(struct ciabase *base);
+extern int cia_get_irq_list(struct ciabase *base, char *buf);
+
+/* irq node variables for amiga interrupt sources */
+static irq_node_t *ami_irq_list[AMI_STD_IRQS];
+
+unsigned short ami_intena_vals[AMI_STD_IRQS] = {
+	IF_VERTB, IF_COPER, IF_AUD0, IF_AUD1, IF_AUD2, IF_AUD3, IF_BLIT,
+	IF_DSKSYN, IF_DSKBLK, IF_RBF, IF_TBE, IF_SOFT, IF_PORTS, IF_EXTER
+};
+static const unsigned char ami_servers[AMI_STD_IRQS] = {
+	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1
+};
 
-static const ushort ami_intena_vals[NUM_AMIGA_SOURCES] = {
-    IF_VERTB, IF_COPER, IF_AUD0, IF_AUD1, IF_AUD2, IF_AUD3, IF_BLIT,
-    IF_DSKSYN, IF_DSKBLK, IF_RBF, IF_TBE, IF_PORTS, IF_PORTS, IF_PORTS,
-    IF_PORTS, IF_PORTS, IF_EXTER, IF_EXTER, IF_EXTER, IF_EXTER, IF_EXTER,
-    IF_SOFT, IF_PORTS, IF_EXTER
-    };
-
-struct ciadata
-{
-    volatile struct CIA *ciaptr;
-    unsigned long	baseirq;
-} ciadata[2];
+static short ami_ablecount[AMI_IRQS];
 
-/*
- * index into ami_lists for IRQs.  CIA IRQs are special, because
- * the same cia interrupt handler is used for both CIAs.
- */
-#define IRQ_IDX(source) (source & ~IRQ_MACHSPEC)
-#define CIA_IRQ_IDX(source) (IRQ_IDX(datap->baseirq) \
-			     +(source-IRQ_AMIGA_CIAA_TA))
+static void ami_badint(int irq, void *dev_id, struct pt_regs *fp)
+{
+	num_spurious += 1;
+}
 
 /*
- * void amiga_init_INTS (void)
+ * void amiga_init_IRQ(void)
  *
  * Parameters:	None
  *
@@ -52,405 +68,428 @@
  * the amiga IRQ handling routines.
  */
 
-static void
-    ami_int1(int irq, struct pt_regs *fp, void *data),
-    ami_int2(int irq, struct pt_regs *fp, void *data),
-    ami_int3(int irq, struct pt_regs *fp, void *data),
-    ami_int4(int irq, struct pt_regs *fp, void *data),
-    ami_int5(int irq, struct pt_regs *fp, void *data),
-    ami_int6(int irq, struct pt_regs *fp, void *data),
-    ami_int7(int irq, struct pt_regs *fp, void *data),
-    ami_intcia(int irq, struct pt_regs *fp, void *data);
-
-void amiga_init_INTS (void)
-{
-    int i;
-
-    /* initialize handlers */
-    for (i = 0; i < NUM_AMIGA_SOURCES; i++)
-	ami_lists[i] = NULL;
-
-    add_isr (IRQ1, ami_int1, 0, NULL, "int1 handler");
-    add_isr (IRQ2, ami_int2, 0, NULL, "int2 handler");
-    add_isr (IRQ3, ami_int3, 0, NULL, "int3 handler");
-    add_isr (IRQ4, ami_int4, 0, NULL, "int4 handler");
-    add_isr (IRQ5, ami_int5, 0, NULL, "int5 handler");
-    add_isr (IRQ6, ami_int6, 0, NULL, "int6 handler");
-    add_isr (IRQ7, ami_int7, 0, NULL, "int7 handler");
-
-    /* hook in the CIA interrupts */
-    ciadata[0].ciaptr = &ciaa;
-    ciadata[0].baseirq = IRQ_AMIGA_CIAA_TA;
-    add_isr (IRQ_AMIGA_PORTS, ami_intcia, 0, NULL, "Amiga CIAA");
-    ciadata[1].ciaptr = &ciab;
-    ciadata[1].baseirq = IRQ_AMIGA_CIAB_TA;
-    add_isr (IRQ_AMIGA_EXTER, ami_intcia, 0, NULL, "Amiga CIAB");
-
-    /* turn off all interrupts and enable the master interrupt bit */
-    custom.intena = 0x7fff;
-    custom.intreq = 0x7fff;
-    custom.intena = 0xc000;
-
-    /* turn off all CIA interrupts */
-    ciaa.icr = 0x7f;
-    ciab.icr = 0x7f;
-
-    /* clear any pending CIA interrupts */
-    i = ciaa.icr;
-    i = ciab.icr;
-}
-
-
-/*
- * The builtin Amiga hardware interrupt handlers.
- */
-
-static void ami_int1 (int irq, struct pt_regs *fp, void *data)
+void amiga_init_IRQ(void)
 {
-    ushort ints = custom.intreqr & custom.intenar;
-
-    /* if serial transmit buffer empty, interrupt */
-    if (ints & IF_TBE) {
-	if (ami_lists[IRQ_IDX(IRQ_AMIGA_TBE)]) {
-	    call_isr_list (IRQ_AMIGA_TBE,
-			   ami_lists[IRQ_IDX(IRQ_AMIGA_TBE)], fp);
-	    /* 
-	     * don't acknowledge.... 
-	     * allow serial code to turn off interrupts, but
-	     * leave it pending so that when interrupts are
-	     * turned on, transmission will resume
-	     */
-	} else
-	    /* acknowledge the interrupt */
-	    custom.intreq = IF_TBE;
-    }
+	int i;
 
-    /* if floppy disk transfer complete, interrupt */
-    if (ints & IF_DSKBLK) {
-	call_isr_list (IRQ_AMIGA_DSKBLK,
-		       ami_lists[IRQ_IDX(IRQ_AMIGA_DSKBLK)], fp);
-
-	/* acknowledge */
-	custom.intreq = IF_DSKBLK;
-    }
-
-    /* if software interrupt set, interrupt */
-    if (ints & IF_SOFT) {
-	call_isr_list (IRQ_AMIGA_SOFT,
-		       ami_lists[IRQ_IDX(IRQ_AMIGA_SOFT)], fp);
-
-	/* acknowledge */
-	custom.intreq = IF_SOFT;
-    }
-}
-
-static void ami_int2 (int irq, struct pt_regs *fp, void *data)
-{
-    ushort ints = custom.intreqr & custom.intenar;
+	/* initialize handlers */
+	for (i = 0; i < AMI_STD_IRQS; i++) {
+		if (ami_servers[i]) {
+			ami_irq_list[i] = NULL;
+		} else {
+			ami_irq_list[i] = new_irq_node();
+			ami_irq_list[i]->handler = ami_badint;
+			ami_irq_list[i]->flags   = IRQ_FLG_STD;
+			ami_irq_list[i]->dev_id  = NULL;
+			ami_irq_list[i]->devname = NULL;
+			ami_irq_list[i]->next    = NULL;
+		}
+	}
+	for (i = 0; i < AMI_IRQS; i++)
+		ami_ablecount[i] = 0;
 
-    if (ints & IF_PORTS) {
-	/* call routines which have hooked into the PORTS interrupt */
-	call_isr_list (IRQ_AMIGA_PORTS,
-		       ami_lists[IRQ_IDX(IRQ_AMIGA_PORTS)], fp);
+	/* turn off all interrupts and enable the master interrupt bit */
+	custom.intena = 0x7fff;
+	custom.intreq = 0x7fff;
+	custom.intena = IF_SETCLR | IF_INTEN;
 
-	/* acknowledge */
-	custom.intreq = IF_PORTS;
-    }
+	cia_init_IRQ(&ciaa_base);
+	cia_init_IRQ(&ciab_base);
 }
 
-static void ami_int3 (int irq, struct pt_regs *fp, void *data)
+void amiga_insert_irq(irq_node_t **list, irq_node_t *node)
 {
-    ushort ints = custom.intreqr & custom.intenar;
+	unsigned long flags;
+	irq_node_t *cur;
 
-    /* if a copper interrupt */
-    if (ints & IF_COPER) {
-	call_isr_list (IRQ_AMIGA_COPPER,
-		       ami_lists[IRQ_IDX(IRQ_AMIGA_COPPER)], fp);
+	if (!node->dev_id)
+		printk("%s: Warning: dev_id of %s is zero\n",
+		       __FUNCTION__, node->devname);
 
-	/* acknowledge */
-	custom.intreq = IF_COPER;
-    }
+	save_flags(flags);
+	cli();
 
-    /* if a vertical blank interrupt */
-    if (ints & IF_VERTB) {
-	call_isr_list (IRQ_AMIGA_VERTB,
-		       ami_lists[IRQ_IDX(IRQ_AMIGA_VERTB)], fp);
+	cur = *list;
 
-	/* acknowledge */
-	custom.intreq = IF_VERTB;
-    }
+	if (node->flags & IRQ_FLG_FAST) {
+		node->flags &= ~IRQ_FLG_SLOW;
+		while (cur && cur->flags & IRQ_FLG_FAST) {
+			list = &cur->next;
+			cur = cur->next;
+		}
+	} else if (node->flags & IRQ_FLG_SLOW) {
+		while (cur) {
+			list = &cur->next;
+			cur = cur->next;
+		}
+	} else {
+		while (cur && !(cur->flags & IRQ_FLG_SLOW)) {
+			list = &cur->next;
+			cur = cur->next;
+		}
+	}
 
-    /* if a blitter interrupt */
-    if (ints & IF_BLIT) {
-	call_isr_list (IRQ_AMIGA_BLIT,
-		       ami_lists[IRQ_IDX(IRQ_AMIGA_BLIT)], fp);
+	node->next = cur;
+	*list = node;
 
-	/* acknowledge */
-	custom.intreq = IF_BLIT;
-    }
+	restore_flags(flags);
 }
 
-static void ami_int4 (int irq, struct pt_regs *fp, void *data)
+void amiga_delete_irq(irq_node_t **list, void *dev_id)
 {
-    ushort ints = custom.intreqr & custom.intenar;
+	unsigned long flags;
+	irq_node_t *node;
 
-    /* if audio 0 interrupt */
-    if (ints & IF_AUD0) {
-	call_isr_list (IRQ_AMIGA_AUD0,
-		       ami_lists[IRQ_IDX(IRQ_AMIGA_AUD0)], fp);
+	save_flags(flags);
+	cli();
 
-	/* acknowledge */
-	custom.intreq = IF_AUD0;
-    }
+	for (node = *list; node; list = &node->next, node = *list) {
+		if (node->dev_id == dev_id) {
+			*list = node->next;
+			/* Mark it as free. */
+			node->handler = NULL;
+			restore_flags(flags);
+			return;
+		}
+	}
+	restore_flags(flags);
+	printk ("%s: tried to remove invalid irq\n", __FUNCTION__);
+}
 
-    /* if audio 1 interrupt */
-    if (ints & IF_AUD1) {
-	call_isr_list (IRQ_AMIGA_AUD1,
-		       ami_lists[IRQ_IDX(IRQ_AMIGA_AUD1)], fp);
+/*
+ * amiga_request_irq : add an interrupt service routine for a particular
+ *                     machine specific interrupt source.
+ *                     If the addition was successful, it returns 0.
+ */
 
-	/* acknowledge */
-	custom.intreq = IF_AUD1;
-    }
+int amiga_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
+                      unsigned long flags, const char *devname, void *dev_id)
+{
+	irq_node_t *node;
 
-    /* if audio 2 interrupt */
-    if (ints & IF_AUD2) {
-	call_isr_list (IRQ_AMIGA_AUD2,
-		       ami_lists[IRQ_IDX(IRQ_AMIGA_AUD2)], fp);
+	if (irq >= AMI_IRQS) {
+		printk ("%s: Unknown IRQ %d from %s\n", __FUNCTION__, irq, devname);
+		return -ENXIO;
+	}
 
-	/* acknowledge */
-	custom.intreq = IF_AUD2;
-    }
+	if (irq >= IRQ_IDX(IRQ_AMIGA_CIAB))
+		return cia_request_irq(&ciab_base, irq - IRQ_IDX(IRQ_AMIGA_CIAB),
+		                       handler, flags, devname, dev_id);
+
+	if (irq >= IRQ_IDX(IRQ_AMIGA_CIAA))
+		return cia_request_irq(&ciaa_base, irq - IRQ_IDX(IRQ_AMIGA_CIAA),
+		                       handler, flags, devname, dev_id);
+
+	if (ami_servers[irq]) {
+		if (!(node = new_irq_node()))
+			return -ENOMEM;
+		node->handler = handler;
+		node->flags   = flags;
+		node->dev_id  = dev_id;
+		node->devname = devname;
+		node->next    = NULL;
+		amiga_insert_irq(&ami_irq_list[irq], node);
+	} else {
+		if (!(ami_irq_list[irq]->flags & IRQ_FLG_STD)) {
+			if (ami_irq_list[irq]->flags & IRQ_FLG_LOCK) {
+				printk("%s: IRQ %d from %s is not replaceable\n",
+				       __FUNCTION__, irq, ami_irq_list[irq]->devname);
+				return -EBUSY;
+			}
+			if (flags & IRQ_FLG_REPLACE) {
+				printk("%s: %s can't replace IRQ %d from %s\n",
+				       __FUNCTION__, devname, irq, ami_irq_list[irq]->devname);
+				return -EBUSY;
+			}
+		}
+		ami_irq_list[irq]->handler = handler;
+		ami_irq_list[irq]->flags   = flags;
+		ami_irq_list[irq]->dev_id  = dev_id;
+		ami_irq_list[irq]->devname = devname;
+	}
 
-    /* if audio 3 interrupt */
-    if (ints & IF_AUD3) {
-	call_isr_list (IRQ_AMIGA_AUD3,
-		       ami_lists[IRQ_IDX(IRQ_AMIGA_AUD3)], fp);
+	/* enable the interrupt */
+	if (irq < IRQ_IDX(IRQ_AMIGA_PORTS)) 
+		custom.intena = IF_SETCLR | ami_intena_vals[irq];
 
-	/* acknowledge */
-	custom.intreq = IF_AUD3;
-    }
+	return 0;
 }
 
-static void ami_int5 (int irq, struct pt_regs *fp, void *data)
+void amiga_free_irq(unsigned int irq, void *dev_id)
 {
-    ushort ints = custom.intreqr & custom.intenar;
+	if (irq >= AMI_IRQS) {
+		printk ("%s: Unknown IRQ %d\n", __FUNCTION__, irq);
+		return;
+	}
 
-    /* if serial receive buffer full interrupt */
-    if (ints & IF_RBF) {
-	if (ami_lists[IRQ_IDX(IRQ_AMIGA_RBF)]) {
-	    call_isr_list (IRQ_AMIGA_RBF,
-			   ami_lists[IRQ_IDX(IRQ_AMIGA_RBF)], fp);
-	    /* don't acknowledge ; leave that for the handler */
-        } else
-	    /* acknowledge the interrupt */
-	    custom.intreq = IF_RBF;
-    }
+	if (irq >= IRQ_IDX(IRQ_AMIGA_CIAB)) {
+		cia_free_irq(&ciab_base, irq - IRQ_IDX(IRQ_AMIGA_CIAB), dev_id);
+		return;
+	}
 
-    /* if a disk sync interrupt */
-    if (ints & IF_DSKSYN) {
-	call_isr_list (IRQ_AMIGA_DSKSYN,
-		       ami_lists[IRQ_IDX(IRQ_AMIGA_DSKSYN)], fp);
+	if (irq >= IRQ_IDX(IRQ_AMIGA_CIAA)) {
+		cia_free_irq(&ciaa_base, irq - IRQ_IDX(IRQ_AMIGA_CIAA), dev_id);
+		return;
+	}
 
-	/* acknowledge */
-	custom.intreq = IF_DSKSYN;
-    }
+	if (ami_servers[irq]) {
+		amiga_delete_irq(&ami_irq_list[irq], dev_id);
+		/* if server list empty, disable the interrupt */
+		if (!ami_irq_list[irq] && irq < IRQ_IDX(IRQ_AMIGA_PORTS))
+			custom.intena = ami_intena_vals[irq];
+	} else {
+		if (ami_irq_list[irq]->dev_id != dev_id)
+			printk("%s: removing probably wrong IRQ %d from %s\n",
+			       __FUNCTION__, irq, ami_irq_list[irq]->devname);
+		ami_irq_list[irq]->handler = ami_badint;
+		ami_irq_list[irq]->flags   = IRQ_FLG_STD;
+		ami_irq_list[irq]->dev_id  = NULL;
+		ami_irq_list[irq]->devname = NULL;
+		custom.intena = ami_intena_vals[irq];
+	}
 }
 
-static void ami_int6 (int irq, struct pt_regs *fp, void *data)
+/*
+ * Enable/disable a particular machine specific interrupt source.
+ * Note that this may affect other interrupts in case of a shared interrupt.
+ * This function should only be called for a _very_ short time to change some
+ * internal data, that may not be changed by the interrupt at the same time.
+ * ami_(enable|disable)_irq calls may also be nested.
+ */
+
+void amiga_enable_irq(unsigned int irq)
 {
-    ushort ints = custom.intreqr & custom.intenar;
+	if (irq >= AMI_IRQS) {
+		printk("%s: Unknown IRQ %d\n", __FUNCTION__, irq);
+		return;
+	}
 
-    if (ints & IF_EXTER) {
-	/* call routines which have hooked into the EXTER interrupt */
-	call_isr_list (IRQ_AMIGA_EXTER,
-		       ami_lists[IRQ_IDX(IRQ_AMIGA_EXTER)], fp);
+	if (ami_ablecount[irq]++)
+		return;
 
-	/* acknowledge */
-	custom.intreq = IF_EXTER;
-    }
-}
+	if (irq >= IRQ_IDX(IRQ_AMIGA_CIAB)) {
+		cia_able_irq(&ciab_base, CIA_ICR_SETCLR |
+		             (1 << (irq - IRQ_IDX(IRQ_AMIGA_CIAB))));
+		return;
+	}
 
-static void ami_int7 (int irq, struct pt_regs *fp, void *data)
-{
-    panic ("level 7 interrupt received\n");
+	if (irq >= IRQ_IDX(IRQ_AMIGA_CIAA)) {
+		cia_able_irq(&ciaa_base, CIA_ICR_SETCLR |
+		             (1 << (irq - IRQ_IDX(IRQ_AMIGA_CIAA))));
+		return;
+	}
+
+	/* enable the interrupt */
+	custom.intena = IF_SETCLR | ami_intena_vals[irq];
 }
 
-static void ami_intcia (int irq, struct pt_regs *fp, void *data)
+void amiga_disable_irq(unsigned int irq)
 {
-    /* check CIA interrupts */
-    struct ciadata *datap;
-    u_char cia_ints;
+	if (irq >= AMI_IRQS) {
+		printk("%s: Unknown IRQ %d\n", __FUNCTION__, irq);
+		return;
+	}
 
-    /* setup data correctly */
-    if (irq == IRQ_AMIGA_PORTS)
-	    datap = &ciadata[0];
-    else
-	    datap = &ciadata[1];
+	if (--ami_ablecount[irq])
+		return;
 
-    cia_ints = datap->ciaptr->icr;
+	if (irq >= IRQ_IDX(IRQ_AMIGA_CIAB)) {
+		cia_able_irq(&ciab_base, 1 << (irq - IRQ_IDX(IRQ_AMIGA_CIAB)));
+		return;
+	}
 
-    /* if timer A interrupt */
-    if (cia_ints & CIA_ICR_TA)
-	call_isr_list (IRQ_AMIGA_CIAA_TA,
-		       ami_lists[CIA_IRQ_IDX(IRQ_AMIGA_CIAA_TA)], fp);
+	if (irq >= IRQ_IDX(IRQ_AMIGA_CIAA)) {
+		cia_able_irq(&ciaa_base, 1 << (irq - IRQ_IDX(IRQ_AMIGA_CIAA)));
+		return;
+	}
 
-    /* if timer B interrupt */
-    if (cia_ints & CIA_ICR_TB)
-	call_isr_list (IRQ_AMIGA_CIAA_TB,
-		       ami_lists[CIA_IRQ_IDX(IRQ_AMIGA_CIAA_TB)], fp);
+	/* disable the interrupt */
+	custom.intena = ami_intena_vals[irq];
+}
 
-    /* if the alarm interrupt */
-    if (cia_ints & CIA_ICR_ALRM)
-	call_isr_list (IRQ_AMIGA_CIAA_ALRM,
-		       ami_lists[CIA_IRQ_IDX(IRQ_AMIGA_CIAA_ALRM)], fp);
+inline void amiga_do_irq(int irq, struct pt_regs *fp)
+{
+	kstat.interrupts[SYS_IRQS + irq]++;
+	ami_irq_list[irq]->handler(irq | IRQ_MACHSPEC, ami_irq_list[irq]->dev_id, fp);
+}
 
-    /* if serial port interrupt (keyboard) */
-    if (cia_ints & CIA_ICR_SP)
-	call_isr_list (IRQ_AMIGA_CIAA_SP,
-		       ami_lists[CIA_IRQ_IDX(IRQ_AMIGA_CIAA_SP)], fp);
+void amiga_do_irq_list(int irq, struct pt_regs *fp, struct irq_server *server)
+{
+	irq_node_t *node, *slow_nodes;
+	int mach_irq = irq | IRQ_MACHSPEC;
+	unsigned short flags;
 
-    /* if flag interrupt (parallel port) */
-    if (cia_ints & CIA_ICR_FLG)
-	call_isr_list (IRQ_AMIGA_CIAA_FLG,
-		       ami_lists[CIA_IRQ_IDX(IRQ_AMIGA_CIAA_FLG)], fp);
+	kstat.interrupts[SYS_IRQS + irq]++;
+	if (server->count++)
+		server->reentrance = 1;
+	/* serve first fast and normal handlers */
+	for (node = ami_irq_list[irq];
+	     node && (!(node->flags & IRQ_FLG_SLOW));
+	     node = node->next)
+		node->handler(mach_irq, node->dev_id, fp);
+	custom.intreq = ami_intena_vals[irq];
+	if (!node) {
+		server->count--;
+		return;
+	}
+	save_flags(flags);
+	restore_flags((flags & ~0x0700) | (fp->sr & 0x0700));
+	/* if slow handlers exists, serve them now */
+	slow_nodes = node;
+	for (;;) {
+		for (; node; node = node->next)
+			node->handler(mach_irq, node->dev_id, fp);
+		/* if reentrance occured, serve slow handlers again */
+		custom.intena = ami_intena_vals[irq];
+		if (!server->reentrance) {
+			server->count--;
+			custom.intena = IF_SETCLR | ami_intena_vals[irq];
+			return;
+		}
+		server->reentrance = 0;
+		custom.intena = IF_SETCLR | ami_intena_vals[irq];
+		node = slow_nodes;
+	}
 }
 
 /*
- * amiga_add_isr : add an interrupt service routine for a particular
- *		   machine specific interrupt source.
- *		   If the addition was successful, it returns 1, otherwise
- *		   it returns 0.  It will fail if another routine is already
- *		   bound into the specified source.
- *   Note that the "pri" argument is currently unused.
+ * The builtin Amiga hardware interrupt handlers.
  */
 
-int amiga_add_isr (unsigned long source, isrfunc isr, int pri, void
-		   *data, char *name)
+static void ami_int1(int irq, void *dev_id, struct pt_regs *fp)
 {
-    unsigned long amiga_source = source & ~IRQ_MACHSPEC;
-    isr_node_t *p;
-
-    if (amiga_source > NUM_AMIGA_SOURCES) {
-	printk ("amiga_add_isr: Unknown interrupt source %ld\n", source);
-	return 0;
-    }
-
-    p = new_isr_node();
-    if (p == NULL)
-	return 0;
-    p->isr = isr;
-    p->pri = pri;
-    p->data = data;
-    p->name = name;
-    p->next = NULL;
-    insert_isr (&ami_lists[amiga_source], p);
-
-    /* enable the interrupt */
-    custom.intena = IF_SETCLR | ami_intena_vals[amiga_source];
+	unsigned short ints = custom.intreqr & custom.intenar;
 
-    /* if a CIAA interrupt, enable the appropriate CIA ICR bit */
-    if (source >= IRQ_AMIGA_CIAA_TA && source <= IRQ_AMIGA_CIAA_FLG)
-	ciaa.icr = 0x80 | (1 << (source - IRQ_AMIGA_CIAA_TA));
+	/* if serial transmit buffer empty, interrupt */
+	if (ints & IF_TBE) {
+		custom.intreq = IF_TBE;
+		amiga_do_irq(IRQ_IDX(IRQ_AMIGA_TBE), fp);
+	}
 
-    /* if a CIAB interrupt, enable the appropriate CIA ICR bit */
-    if (source >= IRQ_AMIGA_CIAB_TA && source <= IRQ_AMIGA_CIAB_FLG)
-	ciab.icr = 0x80 | (1 << (source - IRQ_AMIGA_CIAB_TA));
+	/* if floppy disk transfer complete, interrupt */
+	if (ints & IF_DSKBLK) {
+		custom.intreq = IF_DSKBLK;
+		amiga_do_irq(IRQ_IDX(IRQ_AMIGA_DSKBLK), fp);
+	}
 
-    return 1;
+	/* if software interrupt set, interrupt */
+	if (ints & IF_SOFT) {
+		custom.intreq = IF_SOFT;
+		amiga_do_irq(IRQ_IDX(IRQ_AMIGA_SOFT), fp);
+	}
 }
 
-int amiga_remove_isr (unsigned long source, isrfunc isr, void *data)
+static void ami_int3(int irq, void *dev_id, struct pt_regs *fp)
 {
-    unsigned long amiga_source = source & ~IRQ_MACHSPEC;
-
-    if (amiga_source > NUM_AMIGA_SOURCES) {
-	printk ("amiga_remove_isr: Unknown interrupt source %ld\n", source);
-	return 0;
-    }
-
-    delete_isr (&ami_lists[amiga_source], isr, data);
+	unsigned short ints = custom.intreqr & custom.intenar;
+	static struct irq_server server = {0, 0};
 
-    if (ami_lists[amiga_source] == NULL) {
-	/* disable the interrupt */
-	custom.intena = ami_intena_vals[amiga_source];
+	/* if a blitter interrupt */
+	if (ints & IF_BLIT) {
+		custom.intreq = IF_BLIT;
+		amiga_do_irq(IRQ_IDX(IRQ_AMIGA_BLIT), fp);
+	}
 
-	/* if a CIAA interrupt, disable the appropriate CIA ICR bit */
-	if (source >= IRQ_AMIGA_CIAA_TA && source <= IRQ_AMIGA_CIAA_FLG)
-	    ciaa.icr = 1 << (source - IRQ_AMIGA_CIAA_TA);
-
-	/* if a CIAB interrupt, disable the appropriate CIA ICR bit */
-	if (source >= IRQ_AMIGA_CIAB_TA && source <= IRQ_AMIGA_CIAB_FLG)
-	    ciab.icr = 1 << (source - IRQ_AMIGA_CIAB_TA);
-    }
+	/* if a copper interrupt */
+	if (ints & IF_COPER) {
+		custom.intreq = IF_COPER;
+		amiga_do_irq(IRQ_IDX(IRQ_AMIGA_COPPER), fp);
+	}
 
-    return 1;
+	/* if a vertical blank interrupt */
+	if (ints & IF_VERTB)
+		amiga_do_irq_list(IRQ_IDX(IRQ_AMIGA_VERTB), fp, &server);
 }
 
-
-/*
- * Enable/disable a particular machine specific interrupt source.
- * Note that this may affect other interrupts in case of a shared interrupt.
- */
-
-void amiga_enable_irq(unsigned int source)
+static void ami_int4(int irq, void *dev_id, struct pt_regs *fp)
 {
-    unsigned long amiga_source = source & ~IRQ_MACHSPEC;
+	unsigned short ints = custom.intreqr & custom.intenar;
 
-    if (amiga_source > NUM_AMIGA_SOURCES) {
-	printk("amiga_enable_irq: Unknown interrupt source %d\n", source);
-	return;
-    }
-
-    /* enable the interrupt */
-    custom.intena = IF_SETCLR | ami_intena_vals[amiga_source];
+	/* if audio 0 interrupt */
+	if (ints & IF_AUD0) {
+		custom.intreq = IF_AUD0;
+		amiga_do_irq(IRQ_IDX(IRQ_AMIGA_AUD0), fp);
+	}
 
-    /* if a CIAA interrupt, enable the appropriate CIA ICR bit */
-    if (source >= IRQ_AMIGA_CIAA_TA && source <= IRQ_AMIGA_CIAA_FLG)
-	ciaa.icr = 0x80 | (1 << (source - IRQ_AMIGA_CIAA_TA));
+	/* if audio 1 interrupt */
+	if (ints & IF_AUD1) {
+		custom.intreq = IF_AUD1;
+		amiga_do_irq(IRQ_IDX(IRQ_AMIGA_AUD1), fp);
+	}
 
-    /* if a CIAB interrupt, enable the appropriate CIA ICR bit */
-    if (source >= IRQ_AMIGA_CIAB_TA && source <= IRQ_AMIGA_CIAB_FLG)
-	ciab.icr = 0x80 | (1 << (source - IRQ_AMIGA_CIAB_TA));
+	/* if audio 2 interrupt */
+	if (ints & IF_AUD2) {
+		custom.intreq = IF_AUD2;
+		amiga_do_irq(IRQ_IDX(IRQ_AMIGA_AUD2), fp);
+	}
 
+	/* if audio 3 interrupt */
+	if (ints & IF_AUD3) {
+		custom.intreq = IF_AUD3;
+		amiga_do_irq(IRQ_IDX(IRQ_AMIGA_AUD3), fp);
+	}
 }
 
-void amiga_disable_irq(unsigned int source)
+static void ami_int5(int irq, void *dev_id, struct pt_regs *fp)
 {
-    unsigned long amiga_source = source & ~IRQ_MACHSPEC;
-
-    if (amiga_source > NUM_AMIGA_SOURCES) {
-	printk("amiga_disable_irq: Unknown interrupt source %d\n", source);
-	return;
-    }
+	unsigned short ints = custom.intreqr & custom.intenar;
 
-    /* disable the interrupt */
-    custom.intena = ami_intena_vals[amiga_source];
-
-    /* if a CIAA interrupt, disable the appropriate CIA ICR bit */
-    if (source >= IRQ_AMIGA_CIAA_TA && source <= IRQ_AMIGA_CIAA_FLG)
-	ciaa.icr = 1 << (source - IRQ_AMIGA_CIAA_TA);
+	/* if serial receive buffer full interrupt */
+	if (ints & IF_RBF) {
+		/* acknowledge of IF_RBF must be done by the serial interrupt */
+		amiga_do_irq(IRQ_IDX(IRQ_AMIGA_RBF), fp);
+	}
 
-    /* if a CIAB interrupt, disable the appropriate CIA ICR bit */
-    if (source >= IRQ_AMIGA_CIAB_TA && source <= IRQ_AMIGA_CIAB_FLG)
-	ciab.icr = 1 << (source - IRQ_AMIGA_CIAB_TA);
+	/* if a disk sync interrupt */
+	if (ints & IF_DSKSYN) {
+		custom.intreq = IF_DSKSYN;
+		amiga_do_irq(IRQ_IDX(IRQ_AMIGA_DSKSYN), fp);
+	}
+}
 
+static void ami_int7(int irq, void *dev_id, struct pt_regs *fp)
+{
+	panic ("level 7 interrupt received\n");
 }
 
+void (*amiga_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = {
+	ami_badint, ami_int1, ami_badint, ami_int3,
+	ami_int4, ami_int5, ami_badint, ami_int7
+};
 
-int amiga_get_irq_list( char *buf, int len )
-{	int			i;
-    isr_node_t	*p;
+int amiga_get_irq_list(char *buf)
+{
+	int i, len = 0;
+	irq_node_t *node;
 
-	for( i = 0; i < NUM_AMIGA_SOURCES; ++i ) {
-		if (!ami_lists[i])
+	for (i = 0; i < AMI_STD_IRQS; i++) {
+		if (!(node = ami_irq_list[i]))
 			continue;
-		len += sprintf( buf+len, "ami  %2d: ???????? ", i );
-		for( p = ami_lists[i]; p; p = p->next ) {
-			len += sprintf( buf+len, "%s\n", p->name );
-			if (p->next)
-				len += sprintf( buf+len, "                  " );
-		}
+		if (node->flags & IRQ_FLG_STD)
+			continue;
+		len += sprintf(buf+len, "ami  %2d: %10u ", i,
+		               kstat.interrupts[SYS_IRQS + i]);
+		do {
+			if (ami_servers[i]) {
+				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, "  ");
+			} else {
+				if (node->flags & IRQ_FLG_LOCK)
+					len += sprintf(buf+len, "L ");
+				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 );
+
+	len += cia_get_irq_list(&ciaa_base, buf+len);
+	len += cia_get_irq_list(&ciab_base, buf+len);
+	return len;
 }
diff -urN pc-2.0.21/arch/m68k/amiga/amikeyb.c linux-2.0.21/arch/m68k/amiga/amikeyb.c
--- pc-2.0.21/arch/m68k/amiga/amikeyb.c	Mon May 20 06:54:25 1996
+++ linux-2.0.21/arch/m68k/amiga/amikeyb.c	Sat Aug 24 14:28:55 1996
@@ -1,7 +1,7 @@
 /*
- * linux/amiga/amikeyb.c
+ * linux/arch/m68k/amiga/amikeyb.c
  *
- * Amiga Keyboard driver for 680x0 Linux
+ * Amiga Keyboard driver for Linux/m68k
  *
  * 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
@@ -21,18 +21,19 @@
 #include <linux/timer.h>
 #include <linux/kd.h>
 #include <linux/random.h>
+#include <linux/kernel.h>
 
-#include <asm/bootinfo.h>
+#include <asm/setup.h>
 #include <asm/amigatypes.h>
 #include <asm/amigaints.h>
 #include <asm/amigahw.h>
 #include <asm/irq.h>
 
-extern int do_poke_blanked_console;
-extern void process_keycode (int);
+extern void handle_scancode(unsigned char);
 
 #define AMIKEY_CAPS	(0x62)
 #define BREAK_MASK	(0x80)
+#define RESET_WARNING	(0xf0)	/* before rotation */
 
 static u_short amiplain_map[NR_KEYS] = {
 	0xf060, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037,
@@ -46,7 +47,7 @@
 	0xf020, 0xf07f, 0xf009, 0xf30e, 0xf201, 0xf01b, 0xf07f, 0xf200,
 	0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601,
 	0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, 0xf107,
-	0xf108, 0xf109, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf10a,
+	0xf108, 0xf109, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf30a, 0xf11b,
 	0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
@@ -65,7 +66,7 @@
 	0xf020, 0xf07f, 0xf009, 0xf30e, 0xf201, 0xf01b, 0xf07f, 0xf200,
 	0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601,
 	0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, 0xf111,
-	0xf112, 0xf113, 0xf208, 0xf203, 0xf30d, 0xf30c, 0xf30a, 0xf10a,
+	0xf112, 0xf113, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf203,
 	0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
@@ -84,7 +85,7 @@
 	0xf200, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601,
 	0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 0xf511, 0xf512, 0xf513,
-	0xf514, 0xf515, 0xf208, 0xf202, 0xf30d, 0xf30c, 0xf30a, 0xf516,
+	0xf514, 0xf515, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf204,
 	0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
@@ -103,7 +104,7 @@
 	0xf000, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601,
 	0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, 0xf107,
-	0xf108, 0xf109, 0xf208, 0xf204, 0xf30d, 0xf30c, 0xf30a, 0xf10a,
+	0xf108, 0xf109, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf202,
 	0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
@@ -122,7 +123,7 @@
 	0xf200, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf200, 0xf200, 0xf208, 0xf200, 0xf30d, 0xf30c, 0xf30a, 0xf200,
+	0xf200, 0xf200, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf200,
 	0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
@@ -141,7 +142,7 @@
 	0xf820, 0xf87f, 0xf809, 0xf30e, 0xf80d, 0xf81b, 0xf87f, 0xf200,
 	0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601,
 	0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 0xf505, 0xf506, 0xf507,
-	0xf508, 0xf509, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf50a,
+	0xf508, 0xf509, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf204,
 	0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
@@ -160,7 +161,7 @@
 	0xf200, 0xf07f, 0xf200, 0xf30e, 0xf201, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf30b, 0xf200, 0xf603, 0xf600, 0xf602, 0xf601,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf200, 0xf200, 0xf208, 0xf200, 0xf30d, 0xf30c, 0xf30a, 0xf200,
+	0xf200, 0xf200, 0xf208, 0xf209, 0xf30d, 0xf30c, 0xf30a, 0xf200,
 	0xf700, 0xf700, 0xf207, 0xf702, 0xf703, 0xf701, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
@@ -175,30 +176,31 @@
 static unsigned int key_repeat_rate  = DEFAULT_KEYB_REP_RATE;
 
 static unsigned char rep_scancode;
-static void amikeyb_rep (unsigned long ignore);
+static void amikeyb_rep(unsigned long ignore);
 static struct timer_list amikeyb_rep_timer = {NULL, NULL, 0, 0, amikeyb_rep};
 
 extern struct pt_regs *pt_regs;
 
-static void amikeyb_rep (unsigned long ignore)
+static void amikeyb_rep(unsigned long ignore)
 {
-	unsigned long flags;
-	save_flags(flags);
-	cli();
-
-	pt_regs = NULL;
-
-	amikeyb_rep_timer.expires = jiffies + key_repeat_rate;
-	amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL;
-	add_timer(&amikeyb_rep_timer);
-	process_keycode (rep_scancode);
+    unsigned long flags;
+    save_flags(flags);
+    cli();
+
+    pt_regs = NULL;
+
+    amikeyb_rep_timer.expires = jiffies + key_repeat_rate;
+    amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL;
+    add_timer(&amikeyb_rep_timer);
+    handle_scancode(rep_scancode);
 
-	restore_flags(flags);
+    restore_flags(flags);
 }
 
-static void keyboard_interrupt (int irq, struct pt_regs *fp, void *dummy)
+static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp)
 {
     unsigned char scancode, break_flag;
+    static int reset_warning = 0;
 
     /* save frame for register dump */
     pt_regs = (struct pt_regs *)fp;
@@ -208,49 +210,94 @@
 
     /* switch SP pin to output for handshake */
     ciaa.cra |= 0x40;
+
+    /*
+     *  On receipt of the second RESET_WARNING, we must not pull KDAT high
+     *  again to delay the hard reset as long as possible.
+     *
+     *  Note that not all keyboards send reset warnings...
+     */
+    if (reset_warning)
+	if (scancode == RESET_WARNING) {
+	    printk(KERN_ALERT "amikeyb: Ctrl-Amiga-Amiga reset warning!!\n"
+		   "The system will be reset within 10 seconds!!\n");
+	    /* Panic doesn't sync from within an interrupt, so we do nothing */
+	    return;
+	} else
+	    /* Probably a mistake, cancel the alert */
+	    reset_warning = 0;
+
     /* wait until 85 us have expired */
     udelay(85);
     /* switch CIA serial port to input mode */
     ciaa.cra &= ~0x40;
 
+    mark_bh(KEYBOARD_BH);
+
     /* rotate scan code to get up/down bit in proper position */
     __asm__ __volatile__ ("rorb #1,%0" : "=g" (scancode) : "0" (scancode));
 
     /*
-     * do machine independent keyboard processing of "normalized" scancode
-     * A "normalized" scancode is one that an IBM PC might generate
      * Check make/break first
      */
     break_flag = scancode & BREAK_MASK;
     scancode &= (unsigned char )~BREAK_MASK;
 
     if (scancode == AMIKEY_CAPS) {
-	    /* if the key is CAPS, fake a press/release. */
-	    process_keycode (AMIKEY_CAPS);
-	    process_keycode (BREAK_MASK | AMIKEY_CAPS);
-    } else {
-	    /* handle repeat */
-	    if (break_flag) {
-		    del_timer(&amikeyb_rep_timer);
-		    rep_scancode = 0;
-	    } else {
-		    del_timer(&amikeyb_rep_timer);
-		    rep_scancode = break_flag | scancode;
-		    amikeyb_rep_timer.expires = jiffies + key_repeat_delay;
-		    amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL;
-		    add_timer(&amikeyb_rep_timer);
-	    }
-	    process_keycode (break_flag | scancode);
-    }
-
-    do_poke_blanked_console = 1;
-    mark_bh(CONSOLE_BH);
-    add_keyboard_randomness(scancode);
-
-    return;
+	/* if the key is CAPS, fake a press/release. */
+	handle_scancode(AMIKEY_CAPS);
+	handle_scancode(BREAK_MASK | AMIKEY_CAPS);
+    } else if (scancode < 0x78) {
+	/* handle repeat */
+	if (break_flag) {
+	    del_timer(&amikeyb_rep_timer);
+	    rep_scancode = 0;
+	} else {
+	    del_timer(&amikeyb_rep_timer);
+	    rep_scancode = scancode;
+	    amikeyb_rep_timer.expires = jiffies + key_repeat_delay;
+	    amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL;
+	    add_timer(&amikeyb_rep_timer);
+	}
+	handle_scancode(break_flag | scancode);
+    } else
+	switch (scancode) {
+	    case 0x78:
+		reset_warning = 1;
+		break;
+	    case 0x79:
+		printk(KERN_WARNING "amikeyb: keyboard lost sync\n");
+		break;
+	    case 0x7a:
+		printk(KERN_WARNING "amikeyb: keyboard buffer overflow\n");
+		break;
+#if 0 /* obsolete according to the HRM */
+	    case 0x7b:
+		printk(KERN_WARNING "amikeyb: keyboard controller failure\n");
+		break;
+#endif
+	    case 0x7c:
+		printk(KERN_ERR "amikeyb: keyboard selftest failure\n");
+		break;
+	    case 0x7d:
+		printk(KERN_INFO "amikeyb: initiate power-up key stream\n");
+		break;
+	    case 0x7e:
+		printk(KERN_INFO "amikeyb: terminate power-up key stream\n");
+		break;
+#if 0 /* obsolete according to the HRM */
+	    case 0x7f:
+		printk(KERN_WARNING "amikeyb: keyboard interrupt\n");
+		break;
+#endif
+	    default:
+		printk(KERN_WARNING "amikeyb: unknown keyboard communication code 0x%02x\n",
+		       break_flag | scancode);
+		break;
+	}
 }
 
-int amiga_keyb_init (void)
+int amiga_keyb_init(void)
 {
     if (!AMIGAHW_PRESENT(AMI_KEYBOARD))
         return -EIO;
@@ -263,7 +310,7 @@
     key_maps[5]  = amishift_ctrl_map;
     key_maps[8]  = amialt_map;
     key_maps[12] = amictrl_alt_map;
-    memcpy (plain_map, amiplain_map, sizeof(plain_map));
+    memcpy(plain_map, amiplain_map, sizeof(plain_map));
 
     /*
      * Initialize serial data direction.
@@ -273,28 +320,27 @@
     /*
      * arrange for processing of keyboard interrupt
      */
-    add_isr (IRQ_AMIGA_CIAA_SP, keyboard_interrupt, 0, NULL, "keyboard");
+    request_irq(IRQ_AMIGA_CIAA_SP, keyboard_interrupt, 0, "keyboard", NULL);
 
     return 0;
 }
 
 int amiga_kbdrate( struct kbd_repeat *k )
-
 {
-	if (k->delay > 0) {
-		/* convert from msec to jiffies */
-		key_repeat_delay = (k->delay * HZ + 500) / 1000;
-		if (key_repeat_delay < 1)
-			key_repeat_delay = 1;
-	}
-	if (k->rate > 0) {
-		key_repeat_rate = (k->rate * HZ + 500) / 1000;
-		if (key_repeat_rate < 1)
-			key_repeat_rate = 1;
-	}
+    if (k->delay > 0) {
+	/* convert from msec to jiffies */
+	key_repeat_delay = (k->delay * HZ + 500) / 1000;
+	if (key_repeat_delay < 1)
+            key_repeat_delay = 1;
+    }
+    if (k->rate > 0) {
+	key_repeat_rate = (k->rate * HZ + 500) / 1000;
+	if (key_repeat_rate < 1)
+            key_repeat_rate = 1;
+    }
 
-	k->delay = key_repeat_delay * 1000 / HZ;
-	k->rate  = key_repeat_rate  * 1000 / HZ;
-	
-	return( 0 );
+    k->delay = key_repeat_delay * 1000 / HZ;
+    k->rate  = key_repeat_rate  * 1000 / HZ;
+    
+    return( 0 );
 }
diff -urN pc-2.0.21/arch/m68k/amiga/amisound.c linux-2.0.21/arch/m68k/amiga/amisound.c
--- pc-2.0.21/arch/m68k/amiga/amisound.c	Mon May 20 06:54:25 1996
+++ linux-2.0.21/arch/m68k/amiga/amisound.c	Sat Aug 24 14:28:56 1996
@@ -1,7 +1,7 @@
 /*
- * linux/amiga/amisound.c
+ * linux/arch/m68k/amiga/amisound.c
  *
- * amiga sound driver for 680x0 Linux
+ * amiga sound driver for Linux/m68k
  *
  * 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
@@ -11,9 +11,9 @@
 #include <linux/sched.h>
 #include <linux/timer.h>
 
+#include <asm/amigatypes.h>
 #include <asm/system.h>
 #include <asm/amigahw.h>
-#include <asm/bootinfo.h>
 
 static u_short *snd_data = NULL;
 static const signed char sine_data[] = {
diff -urN pc-2.0.21/arch/m68k/amiga/chipram.c linux-2.0.21/arch/m68k/amiga/chipram.c
--- pc-2.0.21/arch/m68k/amiga/chipram.c	Mon May 20 06:54:25 1996
+++ linux-2.0.21/arch/m68k/amiga/chipram.c	Sat Aug 24 14:28:57 1996
@@ -8,7 +8,7 @@
 
 #include <linux/types.h>
 #include <linux/kernel.h>
-#include <asm/bootinfo.h>
+#include <asm/setup.h>
 #include <asm/amigahw.h>
 
 struct chip_desc {
@@ -22,6 +22,17 @@
 #define DP(ptr) ((struct chip_desc *)(ptr))
 
 static unsigned long chipsize;
+static unsigned long chipavail; /*MILAN*/
+
+/*MILAN*/
+unsigned long amiga_chip_avail( void )
+{
+#ifdef DEBUG
+   printk("chip_avail : %ld bytes\n",chipavail);
+#endif
+   return chipavail;
+}
+
 
 void amiga_chip_init (void)
 {
@@ -46,6 +57,7 @@
   
   dp->alloced = 0;
   dp->length = chipsize - 2*sizeof(*dp);
+  chipavail = dp->length;  /*MILAN*/
 
 #ifdef DEBUG
   printk ("chipram end boundary is %p, length is %d\n", dp,
@@ -63,7 +75,7 @@
 	size = (size + 7) & ~7;
 
 #ifdef DEBUG
-	printk ("chip_alloc: allocate %ld bytes\n", size);
+   printk("chip_alloc: allocate %ld bytes\n", size);
 #endif
 
 	/*
@@ -121,7 +133,9 @@
 	if ((unsigned long)ptr & 7)
 		panic("chip_alloc: alignment violation\n");
 
-	return ptr;
+    chipavail -= size + (2*sizeof(*dp)); /*MILAN*/
+
+    return ptr;
 }
 
 void amiga_chip_free (void *ptr)
@@ -129,6 +143,10 @@
 	struct chip_desc *sdp = DP(ptr) - 1, *dp2;
 	struct chip_desc *edp = DP((unsigned long)ptr + sdp->length);
 
+    chipavail += sdp->length + (2* sizeof(sdp)); /*MILAN*/
+#ifdef DEBUG
+   printk("chip_free: free %ld bytes at %p\n",sdp->length,ptr);
+#endif
 	/* deallocate the chunk */
 	sdp->alloced = edp->alloced = 0;
 
diff -urN pc-2.0.21/arch/m68k/amiga/cia.c linux-2.0.21/arch/m68k/amiga/cia.c
--- pc-2.0.21/arch/m68k/amiga/cia.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/arch/m68k/amiga/cia.c	Sat Aug 24 14:28:58 1996
@@ -0,0 +1,190 @@
+/*
+ *  linux/arch/m68k/amiga/cia.c - CIA support
+ *
+ *  Copyright (C) 1996 Roman Zippel
+ *
+ *  The concept of some functions bases on the original Amiga OS function
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/kernel_stat.h>
+
+#include <asm/irq.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+struct ciabase {
+	volatile struct CIA *cia;
+	u_char icr_mask, icr_data;
+	u_short int_mask;
+	int handler_irq, cia_irq, server_irq;
+	char *name;
+	struct irq_server server;
+	irq_handler_t irq_list[CIA_IRQS];
+} ciaa_base = {
+	&ciaa, 0, 0, IF_PORTS,
+	IRQ2, IRQ_AMIGA_CIAA,
+	IRQ_IDX(IRQ_AMIGA_PORTS),
+	"CIAA handler", {0, 0}
+}, ciab_base = {
+	&ciab, 0, 0, IF_EXTER,
+	IRQ6, IRQ_AMIGA_CIAB,
+	IRQ_IDX(IRQ_AMIGA_EXTER),
+	"CIAB handler", {0, 0}
+};
+
+/*
+ *  Cause or clear CIA interrupts, return old interrupt status.
+ */
+
+unsigned char cia_set_irq(struct ciabase *base, unsigned char mask)
+{
+	u_char old;
+
+	old = (base->icr_data |= base->cia->icr);
+	if (mask & CIA_ICR_SETCLR)
+		base->icr_data |= mask;
+	else
+		base->icr_data &= ~mask;
+	if (base->icr_data & base->icr_mask)
+		custom.intreq = IF_SETCLR | base->int_mask;
+	return (old & base->icr_mask);
+}
+
+/*
+ *  Enable or disable CIA interrupts, return old interrupt mask,
+ *  interrupts will only be enabled if a handler exists
+ */
+
+unsigned char cia_able_irq(struct ciabase *base, unsigned char mask)
+{
+	u_char old, tmp;
+	int i;
+
+	old = base->icr_mask;
+	base->icr_data |= base->cia->icr;
+	base->cia->icr = mask;
+	if (mask & CIA_ICR_SETCLR)
+		base->icr_mask |= mask;
+	else
+		base->icr_mask &= ~mask;
+	base->icr_mask &= CIA_ICR_ALL;
+	for (i = 0, tmp = 1; i < CIA_IRQS; i++, tmp <<= 1) {
+		if ((tmp & base->icr_mask) && !base->irq_list[i].handler)
+			base->icr_mask &= ~tmp;
+ 	}
+	if (base->icr_data & base->icr_mask)
+		custom.intreq = IF_SETCLR | base->int_mask;
+	return (old);
+}
+
+int cia_request_irq(struct ciabase *base, unsigned int irq,
+                    void (*handler)(int, void *, struct pt_regs *),
+                    unsigned long flags, const char *devname, void *dev_id)
+{
+	u_char mask;
+
+	if (!(base->irq_list[irq].flags & IRQ_FLG_STD)) {
+		if (base->irq_list[irq].flags & IRQ_FLG_LOCK) {
+			printk("%s: IRQ %ld from %s is not replaceable\n",
+			       __FUNCTION__, IRQ_IDX(base->cia_irq + irq),
+			       base->irq_list[irq].devname);
+			return -EBUSY;
+		}
+		if (flags & IRQ_FLG_REPLACE) {
+			printk("%s: %s can't replace IRQ %ld from %s\n", __FUNCTION__,
+			       devname, IRQ_IDX(base->cia_irq + irq),
+			       base->irq_list[irq].devname);
+			return -EBUSY;
+		}
+	}
+	base->irq_list[irq].handler = handler;
+	base->irq_list[irq].flags   = flags;
+	base->irq_list[irq].dev_id  = dev_id;
+	base->irq_list[irq].devname = devname;
+
+	/* enable the interrupt */
+	mask = 1 << irq;
+	cia_set_irq(base, mask);
+	cia_able_irq(base, CIA_ICR_SETCLR | mask);
+	return 0;
+}
+
+void cia_free_irq(struct ciabase *base, unsigned int irq, void *dev_id)
+{
+	if (base->irq_list[irq].dev_id != dev_id)
+		printk("%s: removing probably wrong IRQ %ld from %s\n",
+		       __FUNCTION__, IRQ_IDX(base->cia_irq + irq),
+		       base->irq_list[irq].devname);
+
+	base->irq_list[irq].handler = NULL;
+	base->irq_list[irq].flags   = IRQ_FLG_STD;
+
+	cia_able_irq(base, 1 << irq);
+}
+
+static void cia_handler(int irq, void *dev_id, struct pt_regs *fp)
+{
+	struct ciabase *base = (struct ciabase *)dev_id;
+	int mach_irq, i;
+	unsigned char ints;
+
+	mach_irq = base->cia_irq;
+	irq = SYS_IRQS + IRQ_IDX(mach_irq);
+	ints = cia_set_irq(base, CIA_ICR_ALL);
+	custom.intreq = base->int_mask;
+	for (i = 0; i < CIA_IRQS; i++, irq++, mach_irq++) {
+		if (ints & 1) {
+			kstat.interrupts[irq]++;
+			base->irq_list[i].handler(mach_irq, base->irq_list[i].dev_id, fp);
+		}
+		ints >>= 1;
+	}
+	amiga_do_irq_list(base->server_irq, fp, &base->server);
+}
+
+void cia_init_IRQ(struct ciabase *base)
+{
+	int i;
+
+	/* init isr handlers */
+	for (i = 0; i < CIA_IRQS; i++) {
+		base->irq_list[i].handler = NULL;
+		base->irq_list[i].flags   = IRQ_FLG_STD;
+	}
+
+	/* clear any pending interrupt and turn off all interrupts */
+	cia_set_irq(base, CIA_ICR_ALL);
+	cia_able_irq(base, CIA_ICR_ALL);
+
+	/* install CIA handler */
+	request_irq(base->handler_irq, cia_handler, IRQ_FLG_LOCK, base->name, base);
+
+	custom.intena = IF_SETCLR | base->int_mask;
+}
+
+int cia_get_irq_list(struct ciabase *base, char *buf)
+{
+	int i, j, len = 0;
+
+	j = IRQ_IDX(base->cia_irq);
+	for (i = 0; i < CIA_IRQS; i++) {
+		if (!(base->irq_list[i].flags & IRQ_FLG_STD)) {
+			len += sprintf(buf+len, "cia  %2d: %10d ", j + i,
+			               kstat.interrupts[SYS_IRQS + j + i]);
+			if (base->irq_list[i].flags & IRQ_FLG_LOCK)
+				len += sprintf(buf+len, "L ");
+			else
+				len += sprintf(buf+len, "  ");
+			len += sprintf(buf+len, "%s\n", base->irq_list[i].devname);
+		}
+	}
+	return len;
+}
diff -urN pc-2.0.21/arch/m68k/amiga/config.c linux-2.0.21/arch/m68k/amiga/config.c
--- pc-2.0.21/arch/m68k/amiga/config.c	Mon May 20 06:54:25 1996
+++ linux-2.0.21/arch/m68k/amiga/config.c	Wed Aug 28 01:01:43 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/amiga/config.c
+ *  linux/arch/m68k/amiga/config.c
  *
  *  Copyright (C) 1993 Hamish Macdonald
  *
@@ -20,11 +20,10 @@
 #include <linux/kd.h>
 #include <linux/tty.h>
 #include <linux/console.h>
-#include <linux/linkage.h>
 
+#include <asm/setup.h>
 #include <asm/system.h>
 #include <asm/pgtable.h>
-#include <asm/bootinfo.h>
 #include <asm/amigahw.h>
 #include <asm/amigaints.h>
 #include <asm/irq.h>
@@ -35,15 +34,20 @@
 
 extern char m68k_debug_device[];
 
-extern void amiga_sched_init(isrfunc handler);
+extern void amiga_sched_init(void (*handler)(int, void *, struct pt_regs *));
+/* amiga specific keyboard functions */
 extern int amiga_keyb_init(void);
 extern int amiga_kbdrate (struct kbd_repeat *);
-extern void amiga_init_INTS (void);
-extern int amiga_add_isr (unsigned long, isrfunc, int, void *, char *);
-extern int amiga_remove_isr (unsigned long, isrfunc, void *);
-extern int amiga_get_irq_list (char *, int);
-extern void amiga_enable_irq(unsigned int);
-extern void amiga_disable_irq(unsigned int);
+/* amiga specific irq functions */
+extern void amiga_init_IRQ (void);
+extern void (*amiga_default_handler[]) (int, void *, struct pt_regs *);
+extern int amiga_request_irq (unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
+                              unsigned long flags, const char *devname, void *dev_id);
+extern int amiga_free_irq (unsigned int irq, void *dev_id);
+extern void amiga_enable_irq (unsigned int);
+extern void amiga_disable_irq (unsigned int);
+extern int amiga_get_irq_list (char *);
+/* amiga specific timer functions */
 extern unsigned long amiga_gettimeoffset (void);
 extern void a3000_gettod (int *, int *, int *, int *, int *, int *);
 extern void a2000_gettod (int *, int *, int *, int *, int *, int *);
@@ -199,7 +203,7 @@
       switch (custom.deniseid & 0xf) {
       case 0x0c:
 	AMIGAHW_SET(DENISE_HR);
-	printk("DENISE_HR");
+	printk("DENISE_HR ");
 	break;
       case 0x08:
 	AMIGAHW_SET(LISA);
@@ -257,9 +261,10 @@
   mach_sched_init      = amiga_sched_init;
   mach_keyb_init       = amiga_keyb_init;
   mach_kbdrate         = amiga_kbdrate;
-  mach_init_INTS       = amiga_init_INTS;
-  mach_add_isr         = amiga_add_isr;
-  mach_remove_isr      = amiga_remove_isr;
+  mach_init_IRQ        = amiga_init_IRQ;
+  mach_default_handler = &amiga_default_handler;
+  mach_request_irq     = amiga_request_irq;
+  mach_free_irq        = amiga_free_irq;
   mach_enable_irq      = amiga_enable_irq;
   mach_disable_irq     = amiga_disable_irq;
   mach_get_irq_list    = amiga_get_irq_list;
@@ -330,60 +335,24 @@
 #endif /* CONFIG_ZORRO */
 }
 
-extern long time_finetune;	/* from kernel/sched.c */
-
 static unsigned short jiffy_ticks;
 
-#if 1 /* ++1.3++ */
-static void timer_wrapper( int irq, struct pt_regs *fp, void *otimerf )
- {
-   unsigned short flags, old_flags;
-
-   ciab.icr = 0x01;
-
-   save_flags(flags);
-   old_flags = (flags & ~0x0700) | (fp->sr & 0x0700);
-   
-   restore_flags(old_flags);
-
-   (*(isrfunc)otimerf)( irq, fp, NULL );
-
-   restore_flags(flags);
-   ciab.icr = 0x81;
-}
-#endif
-
-void amiga_sched_init (isrfunc timer_routine)
+void amiga_sched_init(void (*timer_routine)(int, void *, struct pt_regs *))
 {
+	jiffy_ticks = (amiga_eclock+HZ/2)/HZ;
 
-#if 0 /* XXX */ /* I think finetune was removed by the 1.3.29 patch */
-    double finetune;
-#endif
-
-    jiffy_ticks = (amiga_eclock+50)/100;
-#if 0 /* XXX */
-    finetune = (jiffy_ticks-amiga_eclock/HZ)/amiga_eclock*1000000*(1<<24);
-    time_finetune = finetune+0.5;
-#endif
-
-    ciab.cra &= 0xC0;	 /* turn off timer A, continuous mode, from Eclk */
-    ciab.talo = jiffy_ticks % 256;
-    ciab.tahi = jiffy_ticks / 256;
-    /* CIA interrupts when counter underflows, so adjust ticks by 1 */
-    jiffy_ticks -= 1;
-
-    /* install interrupt service routine for CIAB Timer A */
-    /*
-     * Please don't change this to use ciaa, as it interferes with the
-     * SCSI code. We'll have to take a look at this later
-     */
-#if 0
-    add_isr (IRQ_AMIGA_CIAB_TA, timer_routine, 0, NULL, "timer");
-#else
-    add_isr (IRQ_AMIGA_CIAB_TA, timer_wrapper, 0, timer_routine, "timer");
-#endif
-    /* start timer */
-    ciab.cra |= 0x01;
+	ciab.cra &= 0xC0;	 /* turn off timer A, continuous mode, from Eclk */
+	ciab.talo = jiffy_ticks % 256;
+	ciab.tahi = jiffy_ticks / 256;
+
+	/* install interrupt service routine for CIAB Timer A
+	 *
+	 * Please don't change this to use ciaa, as it interferes with the
+	 * SCSI code. We'll have to take a look at this later
+	 */
+	request_irq(IRQ_AMIGA_CIAB_TA, timer_routine, IRQ_FLG_LOCK, "timer", NULL);
+	/* start timer */
+	ciab.cra |= 0x11;
 }
 
 #define TICK_SIZE 10000
@@ -391,33 +360,30 @@
 /* This is always executed with interrupts disabled.  */
 unsigned long amiga_gettimeoffset (void)
 {
-    unsigned short hi, lo, hi2;
-    unsigned long ticks, offset = 0;
+	unsigned short hi, lo, hi2;
+	unsigned long ticks, offset = 0;
 
-    /* read CIA A timer A current value */
-    hi	= ciab.tahi;
-    lo	= ciab.talo;
-    hi2 = ciab.tahi;
-
-    if (hi != hi2) {
-	lo = ciab.talo;
-	hi = hi2;
-    }
+	/* read CIA B timer A current value */
+	hi  = ciab.tahi;
+	lo  = ciab.talo;
+	hi2 = ciab.tahi;
+
+	if (hi != hi2) {
+		lo = ciab.talo;
+		hi = hi2;
+	}
 
-    ticks = hi << 8 | lo;
+	ticks = hi << 8 | lo;
 
-#if 0 /* XXX */
-/* reading the ICR clears all interrupts.  bad idea! */
-      if (ticks > jiffy_ticks - jiffy_ticks / 100)
-	/* check for pending interrupt */
-	if (ciab.icr & CIA_ICR_TA)
-	  offset = 10000;
-#endif
+	if (ticks > jiffy_ticks / 2)
+		/* check for pending interrupt */
+		if (cia_set_irq(&ciab_base, 0) & CIA_ICR_TA)
+			offset = 10000;
 
-    ticks = (jiffy_ticks-1) - ticks;
-    ticks = (10000 * ticks) / jiffy_ticks;
+	ticks = jiffy_ticks - ticks;
+	ticks = (10000 * ticks) / jiffy_ticks;
 
-    return ticks + offset;
+	return ticks + offset;
 }
 
 void a3000_gettod (int *yearp, int *monp, int *dayp,
@@ -657,7 +623,7 @@
   unsigned long jmp_addr = VTOP(&&jmp_addr_label);
 
   cli();
-  if (m68k_is040or060)
+  if (CPU_IS_040_OR_060)
     /* Setup transparent translation registers for mapping
      * of 16 MB kernel segment before disabling translation
      */
diff -urN pc-2.0.21/arch/m68k/amiga/cyberfb.c linux-2.0.21/arch/m68k/amiga/cyberfb.c
--- pc-2.0.21/arch/m68k/amiga/cyberfb.c	Tue May 21 18:51:42 1996
+++ linux-2.0.21/arch/m68k/amiga/cyberfb.c	Wed Sep 18 22:21:50 1996
@@ -29,11 +29,9 @@
 #include <linux/tty.h>
 #include <linux/malloc.h>
 #include <linux/delay.h>
-#include <linux/config.h>
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <asm/irq.h>
-#include <asm/bootinfo.h>
 #include <asm/zorro.h>
 #include <asm/pgtable.h>
 #include <linux/fb.h>
@@ -331,7 +329,8 @@
   }
 
 *memstart = (*memstart + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
-CyberMem = kernel_map (board_addr + 0x01400000, 0x00400000,
+/* This includes the video memory as well as the S3 register set */
+CyberMem = kernel_map (board_addr + 0x01400000, 0x01000000,
                        KERNELMAP_NOCACHE_SER, memstart);
 
 if (Cyberfb_Cyber8)
@@ -339,8 +338,7 @@
 else
   memset ((char*)CyberMem, 0, CYBER16_WIDTH * CYBER16_HEIGHT);
 
-CyberRegs = (char*) kernel_map (board_addr + 0x02000000, 0xf000,
-                                KERNELMAP_NOCACHE_SER, memstart);
+CyberRegs = (char*) (CyberMem + 0x00c00000);
 
 /* Disable hardware cursor */
 *(CyberRegs + S3_CRTC_ADR)  = S3_REG_LOCK2;
@@ -410,7 +408,11 @@
 
    strcpy(fix->id, Cyber_fb_name);
    fix->smem_start = CyberMem;
+#if 0
    fix->smem_len = CyberSize;
+#else
+   fix->smem_len = 0x01000000;
+#endif
 
    fix->type = FB_TYPE_PACKED_PIXELS;
    fix->type_aux = 0;
diff -urN pc-2.0.21/arch/m68k/amiga/ksyms.c linux-2.0.21/arch/m68k/amiga/ksyms.c
--- pc-2.0.21/arch/m68k/amiga/ksyms.c	Mon May 20 06:54:25 1996
+++ linux-2.0.21/arch/m68k/amiga/ksyms.c	Sat Aug 24 14:29:01 1996
@@ -1,5 +1,7 @@
 #include <linux/module.h>
 #include <asm/zorro.h>
+#include <asm/amigatypes.h>
+#include <asm/amigahw.h>
 
 static struct symbol_table mach_amiga_symbol_table = {
 #include <linux/symtab_begin.h>
@@ -7,11 +9,16 @@
   /*
    * Add things here when you find the need for it.
    */
+  X(amiga_colorclock),
+  X(amiga_chip_alloc),
+  X(amiga_chip_free),
+  X(amiga_chip_avail),
 
   X(zorro_find),
   X(zorro_get_board),
   X(zorro_config_board),
   X(zorro_unconfig_board),
+  X(zorro_unused_z2ram),
 
   /* example
   X(something_you_need),
diff -urN pc-2.0.21/arch/m68k/amiga/zorro.c linux-2.0.21/arch/m68k/amiga/zorro.c
--- pc-2.0.21/arch/m68k/amiga/zorro.c	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/amiga/zorro.c	Sat Aug 24 14:29:03 1996
@@ -13,9 +13,9 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
+#include <asm/setup.h>
 #include <asm/bitops.h>
 #include <asm/amigahw.h>
-#include <asm/bootinfo.h>
 #include <asm/zorro.h>
 
 
@@ -69,12 +69,23 @@
     *             long (max. 80 characters per board identification line)
     */
 
+BEGIN_PROD(PACIFIC)
+   PROD("SE 2000 A500", SE_2000_A500)
+   PROD("HD Controller", PACIFIC_HD)
+END
+
+BEGIN_PROD(KUPKE)
+   PROD("Golem RAM Box 2MB", GOLEM_BOX_2)
+END
+
 BEGIN_PROD(MEMPHIS)
    PROD("Stormbringer", STORMBRINGER)
 END
 
 BEGIN_PROD(COMMODORE2)
-   PROD("A2088 Bridgeboard", A2088)
+   PROD("A2088 XT Bridgeboard", A2088)
+   PROD("A2286 AT Bridgeboard", A2286)
+   PROD("A4091 SCSI Controller", A4091_2)
    PROD("A2386-SX Bridgeboard", A2386SX)
 END
 
@@ -91,22 +102,68 @@
    PROD("A2620 68020/RAM Card", A2620)
    PROD("A2630 68030/RAM Card", A2630)
    PROD("A4091 SCSI Controller", A4091)
+   PROD("A2065 Ethernet Card", A2065_2)
    PROD("Romulator Card", ROMULATOR)
    PROD("A3000 Test Fixture", A3000TESTFIX)
+   PROD("A2386-SX Bridgeboard", A2386SX_2)
    PROD("A2065 Ethernet Card", A2065)
 END
 
+BEGIN_PROD(COMMODORE3)
+   PROD("A2090A Combitec/MacroSystem", A2090A_CM)
+END
+
+BEGIN_PROD(KCS)
+   PROD("KCS Power PC Board", POWER_BOARD)
+END
+
 BEGIN_PROD(CARDCO)
+   PROD("Kronos 2000 SCSI Controller", KRONOS_2000_SCSI)
+   PROD("A1000 SCSI Controller", A1000_SCSI)
+   PROD("Escort SCSI Controller", ESCORT_SCSI)
    PROD("Cardco A2410 Hires Graphics card", CC_A2410)
 END
 
+BEGIN_PROD(A_SQUARED)
+   PROD("Live! 2000", LIVE_2000)
+END
+
+BEGIN_PROD(COMSPEC)
+   PROD("AX2000", AX2000)
+END
+
+BEGIN_PROD(ANAKIN)
+   PROD("Easyl Tablet", EASYL)
+END
+
 BEGIN_PROD(MICROBOTICS)
+   PROD("StarBoard II", STARBOARD_II)
+   PROD("StarDrive", STARDRIVE)
+   PROD("8-Up (Rev A)", 8_UP_A)
+   PROD("8-Up (Rev Z)", 8_UP_Z)
+   PROD("VXL RAM", VXL_RAM)
    PROD("VXL-30 Turbo Board", VXL_30)
+   PROD("MBX 1200", MBX_1200)
+   PROD("Hardframe 2000", HARDFRAME_2000)
+   PROD("MBX 1200", MBX_1200_2)
+END
+
+BEGIN_PROD(ACCESS)
+END
+
+BEGIN_PROD(EXPANSION_TECH)
 END
 
 BEGIN_PROD(ASDG)
+   PROD("Memory Expansion", ASDG_MEMORY)
+   PROD("Memory Expansion", ASDG_MEMORY_2)
    PROD("Lan Rover Ethernet", LAN_ROVER)
-   PROD("Dual Serial Card", ASDG_DUAL_SERIAL)
+   PROD("Twin-X Serial Card", TWIN_X)
+END
+
+BEGIN_PROD(IMTRONICS)
+   PROD("Hurricane 2800 68030", HURRICANE_2800)
+   PROD("Hurricane 2800 68030", HURRICANE_2800_2)
 END
 
 BEGIN_PROD(UNIV_OF_LOWELL)
@@ -120,9 +177,15 @@
 END
 
 BEGIN_PROD(SUPRA)
+   PROD("SupraDrive 4x4 SCSI Controller", SUPRADRIVE_4x4)
+   PROD("2000 DMA HD", SUPRA_2000)
+   PROD("500 HD/RAM", SUPRA_500)
+   PROD("500RX/2000 RAM", SUPRA_500RX)
+   PROD("500RX/2000 RAM", SUPRA_500RX_2)
+   PROD("2400zi Modem", SUPRA_2400ZI)
    PROD("Wordsync SCSI Controller", WORDSYNC)
    PROD("Wordsync II SCSI Controller", WORDSYNC_II)
-   PROD("2400 Modem", SUPRA_2400MODEM)
+   PROD("2400zi+ Modem", SUPRA_2400ZIPLUS)
 END
 
 BEGIN_PROD(CSA)
@@ -130,21 +193,35 @@
    PROD("12 Gauge SCSI Controller", 12GAUGE)
 END
 
+BEGIN_PROD(GVP3)
+   PROD("Impact SCSI/Memory", IMPACT)
+END
+
+BEGIN_PROD(BYTEBOX)
+   PROD("A500", BYTEBOX_A500)
+END
+
 BEGIN_PROD(POWER_COMPUTING)
-   PROD("Viper II Turbo Board (DKB 1240)", DKB_1240)
+   PROD("DKB 3128 RAM", DKB_3128)
+   PROD("DKB Cobra / Viper II Turbo Board", VIPER_II_COBRA)
 END
 
 BEGIN_PROD(GVP)
+   PROD("Impact Series-I SCSI 4K", IMPACT_I_4K)
+   PROD("Impact Series-I SCSI 16K/2", IMPACT_I_16K_2)
+   PROD("Impact Series-I SCSI 16K/3", IMPACT_I_16K_3)
+   PROD("Impact 3001 IDE", IMPACT_3001_IDE)
+/* PROD("Impact 3001 RAM", IMPACT_3001_RAM) */
    PROD("Generic GVP product", GVP)
    PROD("Series II SCSI Controller", GVPIISCSI)
    PROD("Series II SCSI Controller", GVPIISCSI_2)
    PROD("Series II RAM", GVPIIRAM)
    PROD("A2000 68030 Turbo Board", GVP_A2000_030)
+/* PROD("Impact 3001 IDE", IMPACT_3001_IDE_2) */
    PROD("GFORCE 040 with SCSI Controller", GFORCE_040_SCSI)
    PROD("IV-24 Graphics Board", GVPIV_24)
-/*
-   PROD("I/O Extender", GVPIO_EXT)
-*/
+   PROD("GFORCE 040 Turbo Board", GFORCE_040)
+/* PROD("I/O Extender", GVPIO_EXT) */
 END
 
 BEGIN_GVP_PROD
@@ -164,6 +241,14 @@
    GVP_PROD("SERIES-II SCSI controller", SERIESII)
 END
 
+BEGIN_PROD(SYNERGY)
+END
+
+BEGIN_PROD(XETEC)
+   PROD("FastCard SCSI Controller", FASTCARD_SCSI)
+   PROD("FastCard RAM", FASTCARD_RAM)
+END
+
 BEGIN_PROD(PPI)
    PROD("Mercury Turbo Board", MERCURY)
    PROD("PP&S A3000 68040 Turbo Board", PPS_A3000_040)
@@ -172,12 +257,23 @@
    PROD("PP&S A500 68040 Turbo Board", PPS_A500_040)
 END
 
+BEGIN_PROD(SPIRIT)
+   PROD("HDA 506 Harddisk", HDA_506)
+   PROD("OctaByte RAM", OCTABYTE_RAM)
+END
+
 BEGIN_PROD(BSC)
    PROD("ALF 3 SCSI Controller", ALF_3_SCSI)
 END
 
+BEGIN_PROD(BSC3)
+   PROD("ALF 2 SCSI Controller", ALF_2_SCSI)
+   PROD("ALF 3 SCSI Controller", ALF_3_SCSI_2)
+END
+
 BEGIN_PROD(C_LTD)
    PROD("Kronos SCSI Controller", KRONOS_SCSI)
+   PROD("A1000 SCSI Controller", A1000_SCSI_2)
 END
 
 BEGIN_PROD(JOCHHEIM)
@@ -188,26 +284,29 @@
    PROD("Serial Solution", SERIAL_SOLUTION)
 END
 
-BEGIN_PROD(GOLEM)
-   PROD("Golem SCSI-II Controller", GOLEM_SCSI_II)
+BEGIN_PROD(ICD)
+   PROD("Advantage 2000 SCSI Controller", ADVANTAGE_2000)
 END
 
-BEGIN_PROD(HARDITAL_SYNTHES)
-   PROD("SCSI Controller", HARDITAL_SCSI)
+BEGIN_PROD(KUPKE2)
+   PROD("Golem SCSI-II Controller", KUPKE_SCSI_II)
+   PROD("Golem Box", GOLEM_BOX)
+   PROD("Golem SCSI/AT Controller", KUPKE_SCSI_AT)
 END
 
-BEGIN_PROD(HARDITAL2)
+BEGIN_PROD(HARDITAL)
    PROD("TQM 68030+68882 Turbo Board", TQM)
 END
 
 BEGIN_PROD(BSC2)
    PROD("Oktagon 2008 SCSI Controller", OKTAGON_SCSI)
-   PROD("Tandem", TANDEM)
+   PROD("Tandem AT-2008/508 IDE Controller", TANDEM)
    PROD("Oktagon 2008 RAM", OKTAGON_RAM)
    PROD("Alfa Data MultiFace I", MULTIFACE_I)
    PROD("Alfa Data MultiFace II", MULTIFACE_II)
    PROD("Alfa Data MultiFace III", MULTIFACE_III)
-   PROD("ISDN Master", ISDN_MASTER)
+   PROD("ISDN MasterCard", ISDN_MASTERCARD)
+   PROD("ISDN MasterCard II", ISDN_MASTERCARD_2)
 END
 
 BEGIN_PROD(ADV_SYS_SOFT)
@@ -215,13 +314,26 @@
    PROD("Nexus RAM", NEXUS_RAM)
 END
 
+BEGIN_PROD(IMPULSE)
+   PROD("FireCracker 24", FIRECRACKER_24)
+END
+
 BEGIN_PROD(IVS)
-   PROD("Trumpcard 500 SCSI Controller", TRUMPCARD_500)
-   PROD("Trumpcard SCSI Controller", TRUMPCARD)
+   PROD("GrandSlam RAM", GRANDSLAM)
+   PROD("OverDrive HD", IVS_OVERDRIVE)
+   PROD("Trumpcard Classic SCSI Controller", TRUMPCARD_CLASSIC)
+   PROD("Trumpcard Pro SCSI Controller", TRUMPCARD_PRO)
+   PROD("Meta-4 RAM", META_4)
    PROD("Vector SCSI Controller", VECTOR)
 END
 
+BEGIN_PROD(VECTOR)
+   PROD("Connection Serial IO", CONNECTION)
+END
+
 BEGIN_PROD(XPERT_PRODEV)
+   PROD("Visiona Graphics Board (RAM)", VISIONA_RAM)
+   PROD("Visiona Graphics Board (REG)", VISIONA_REG)
    PROD("Merlin Graphics Board (RAM)", MERLIN_RAM)
    PROD("Merlin Graphics Board (REG)", MERLIN_REG)
 END
@@ -230,8 +342,27 @@
    PROD("Amiganet Board", AMIGANET)
 END
 
-BEGIN_PROD(DIG_MICRONICS)
+BEGIN_PROD(SUNRIZE)
+   PROD("AD516 Audio", AD516)
+END
+
+BEGIN_PROD(TRICERATOPS)
+   PROD("Multi I/O Board", TRICERATOPS)
+END
+
+BEGIN_PROD(APPLIED_MAGIC)
    PROD("DMI Resolver Graphics Board", DMI_RESOLVER)
+   PROD("Digital Broadcaster", DIGITAL_BCASTER)
+END
+
+BEGIN_PROD(GFX_BASE)
+   PROD("GDA-1 Graphics Board (RAM)", GDA_1_RAM)
+   PROD("GDA-1 Graphics Board (REG)", GDA_1_REG)
+END
+
+BEGIN_PROD(ROCTEC)
+   PROD("RH 800C Hard Disk Controller", RH_800C)
+   PROD("RH 800C RAM", RH_800C_RAM)
 END
 
 BEGIN_PROD(HELFRICH1)
@@ -242,10 +373,23 @@
    PROD("GG2+ Bus Converter", GG2PLUS)
 END
 
+BEGIN_PROD(MASOBOSHI)
+   PROD("Master Card RAM", MASTER_CARD_RAM)
+   PROD("Master Card SCSI Controller", MASTER_CARD_SCSI)
+   PROD("MVD 819", MVD_819)
+END
+
+BEGIN_PROD(DELACOMP)
+   PROD("RAM Expansion 2000", DELACOMP_RAM_2000)
+END
+
 BEGIN_PROD(VILLAGE_TRONIC)
-   PROD("Ariadne Ethernet Card", ARIADNE)
+   PROD("Domino Graphics Board (RAM)", DOMINO_RAM)
+   PROD("Domino Graphics Board (REG)", DOMINO_REG)
    PROD("Picasso II Graphics Board (RAM)", PICASSO_II_RAM)
    PROD("Picasso II Graphics Board (REG)", PICASSO_II_REG)
+   PROD("Picasso II Graphics Board (REG)", PICASSO_II_REG_2)
+   PROD("Ariadne Ethernet Card", ARIADNE)
 END
 
 BEGIN_PROD(UTILITIES_ULTD)
@@ -253,51 +397,98 @@
    PROD("Emplant Deluxe SCSI Controller", EMPLANT_DELUXE2)
 END
 
+BEGIN_PROD(AMITRIX)
+   PROD("Multi-IO", AMITRIX_MULTI_IO)
+   PROD("CD-RAM Memory", AMITRIX_CD_RAM)
+END
+
 BEGIN_PROD(MTEC)
    PROD("68030 Turbo Board", MTEC_68030)
-   PROD("T1230/28 Turbo Board", MTEC_T1230)
+   PROD("A1200 T68030/42 RTC Turbo Board", MTEC_T1230)
+   PROD("8MB RAM", MTEC_RAM)
 END
 
 BEGIN_PROD(GVP2)
-   PROD("Spectrum Graphics Board (RAM)", SPECTRUM_RAM)
-   PROD("Spectrum Graphics Board (REG)", SPECTRUM_REG)
+   PROD("EGS 28/24 Spectrum Graphics Board (RAM)", SPECTRUM_RAM)
+   PROD("EGS 28/24 Spectrum Graphics Board (REG)", SPECTRUM_REG)
 END
 
 BEGIN_PROD(HELFRICH2)
    PROD("Piccolo Graphics Board (RAM)", PICCOLO_RAM)
    PROD("Piccolo Graphics Board (REG)", PICCOLO_REG)
    PROD("PeggyPlus MPEG Decoder Board", PEGGY_PLUS)
+   PROD("VideoCruncher", VIDEOCRUNCHER)
    PROD("SD64 Graphics Board (RAM)", SD64_RAM)
    PROD("SD64 Graphics Board (REG)", SD64_REG)
 END
 
 BEGIN_PROD(MACROSYSTEMS)
-   PROD("Warp Engine SCSI Controller", WARP_ENGINE)
+   PROD("Warp Engine 40xx SCSI Controller", WARP_ENGINE)
+END
+
+BEGIN_PROD(ELBOX)
+   PROD("Elbox 1200/4 RAM", ELBOX_1200)
 END
 
 BEGIN_PROD(HARMS_PROF)
+   PROD("030 plus", HARMS_030_PLUS)
    PROD("3500 Turbo board", 3500_TURBO)
 END
 
+BEGIN_PROD(MICRONIK)
+   PROD("RCA 120 RAM", RCA_120)
+END
+
+BEGIN_PROD(IMTRONICS2)
+   PROD("Hurricane 2800 68030", HURRICANE_2800_3)
+   PROD("Hurricane 2800 68030", HURRICANE_2800_4)
+END
+
+BEGIN_PROD(KUPKE3)
+   PROD("Golem HD 3000", GOLEM_3000)
+END
+
+BEGIN_PROD(INFORMATION)
+   PROD("ISDN Engine I", ISDN_ENGINE_I)
+END
+
 BEGIN_PROD(VORTEX)
-   PROD("Golden Gate 80386 Board", GOLDEN_GATE_386)
+   PROD("Golden Gate 80386SX Board", GOLDEN_GATE_386SX)
    PROD("Golden Gate RAM", GOLDEN_GATE_RAM)
    PROD("Golden Gate 80486 Board", GOLDEN_GATE_486)
 END
 
 BEGIN_PROD(DATAFLYER)
-   PROD("4000SX SCSI Controller", DATAFLYER_4000SX)
+   PROD("4000SX SCSI Controller", DATAFLYER_4000SXS)
+   PROD("4000SX RAM", DATAFLYER_4000SXR)
+END
+
+BEGIN_PROD(READYSOFT)
+   PROD("AMax II/IV", AMAX)
 END
 
 BEGIN_PROD(PHASE5)
+   PROD("Blizzard RAM", BLIZZARD_RAM)
+   PROD("Blizzard", BLIZZARD)
+   PROD("Blizzard 1220-IV Turbo Board", BLIZZARD_1220_IV)
    PROD("FastLane RAM", FASTLANE_RAM)
-   PROD("FastLane/Blizzard 1230-II SCSI Controller", FASTLANE_SCSI)
-   PROD("CyberStorm Fast SCSI-II Controller", CYBERSTORM_SCSI)
+   PROD("FastLane/Blizzard 1230-II/CyberSCSI", FASTLANE_SCSI)
+   PROD("Blizzard 1220/CyberStorm", CYBERSTORM_SCSI)
    PROD("Blizzard 1230-III Turbo Board", BLIZZARD_1230_III)
-   PROD("Blizzard 1230-IV Turbo Board", BLIZZARD_1230_IV)
+   PROD("Blizzard 1230-IV/1260 Turbo Board", BLIZZARD_1230_IV)
+   PROD("Blizzard 2060 SCSI Controller", BLIZZARD_2060SCSI)
+   PROD("CyberStorm", CYBERSTORM)
    PROD("CyberVision64 Graphics Board", CYBERVISION)
 END
 
+BEGIN_PROD(DPS)
+   PROD("Personal Animation Recorder", DPS_PAR)
+END
+
+BEGIN_PROD(APOLLO2)
+   PROD("A620 68020 Accelerator", A620)
+END
+
 BEGIN_PROD(APOLLO)
    PROD("AT-Apollo", AT_APOLLO)
    PROD("Turbo Board", APOLLO_TURBO)
@@ -313,52 +504,107 @@
    PROD("Maestro Pro", MAESTRO_PRO)
    PROD("Retina Z2 Graphics Board", RETINA_Z2)
    PROD("MultiEvolution", MULTI_EVOLUTION)
+   PROD("Toccata Sound Board", TOCCATA)
    PROD("Retina Z3 Graphics Board", RETINA_Z3)
+   PROD("VLab Motion", VLAB_MOTION)
    PROD("Falcon '040 Turbo Board", FALCON_040)
 END
 
+BEGIN_PROD(COMBITEC)
+END
+
+BEGIN_PROD(SKI)
+   PROD("SCSI / Dual Serial", SKI_SCSI_SERIAL)
+END
+
+BEGIN_PROD(CAMERON)
+   PROD("Scanner Interface", CAMERON_SCANNER)
+END
+
+BEGIN_PROD(REIS_WARE)
+   PROD("Handyscanner", RW_HANDYSCANNER)
+END
+
+
 BEGIN_MANUF
+   MANUF("Pacific Peripherals", PACIFIC)
+   MANUF("Kupke", KUPKE)
    MANUF("Memphis", MEMPHIS)
    MANUF("Commodore", COMMODORE2)
    MANUF("Commodore", COMMODORE)
+   MANUF("Commodore", COMMODORE3)
+   MANUF("Kolff Computer Supplies", KCS)
    MANUF("Cardco", CARDCO)
+   MANUF("A-Squared", A_SQUARED)
+   MANUF("ComSpec Communications", COMSPEC)
+   MANUF("Anakin", ANAKIN)
    MANUF("MicroBotics", MICROBOTICS)
+   MANUF("Access Associates", ACCESS)
+   MANUF("Expansion Technologies", EXPANSION_TECH)
    MANUF("ASDG", ASDG)
+   MANUF("Imtronics", IMTRONICS)
    MANUF("University of Lowell", UNIV_OF_LOWELL)
    MANUF("Ameristar", AMERISTAR)
    MANUF("Supra", SUPRA)
    MANUF("CSA", CSA)
+   MANUF("Great Valley Products", GVP3)
+   MANUF("ByteBox", BYTEBOX)
    MANUF("Power Computing", POWER_COMPUTING)
    MANUF("Great Valley Products", GVP)
+   MANUF("Synergy", SYNERGY)
+   MANUF("Xetec", XETEC)
    MANUF("Progressive Peripherals", PPI)
+   MANUF("Spirit", SPIRIT)
    MANUF("BSC", BSC)
+   MANUF("BSC", BSC3)
    MANUF("C Ltd.", C_LTD)
    MANUF("Jochheim", JOCHHEIM)
    MANUF("Checkpoint Technologies", CHECKPOINT)
-   MANUF("Golem", GOLEM)
-   MANUF("Hardital Synthesis", HARDITAL_SYNTHES)
-   MANUF("Hardital Synthesis", HARDITAL2)
+   MANUF("ICD", ICD)
+   MANUF("Kupke", KUPKE2)
+   MANUF("Hardital Synthesis", HARDITAL)
    MANUF("BSC", BSC2)
    MANUF("Advanced Systems & Software", ADV_SYS_SOFT)
+   MANUF("Impulse", IMPULSE)
    MANUF("IVS", IVS)
+   MANUF("Vector", VECTOR)
    MANUF("XPert/ProDev", XPERT_PRODEV)
    MANUF("Hydra Systems", HYDRA_SYSTEMS)
-   MANUF("Digital Micronics", DIG_MICRONICS)
+   MANUF("Sunrize Industries", SUNRIZE)
+   MANUF("Triceratops", TRICERATOPS)
+   MANUF("Applied Magic", APPLIED_MAGIC)
+   MANUF("GFX-Base", GFX_BASE)
+   MANUF("RocTec", ROCTEC)
    MANUF("Helfrich", HELFRICH1)
    MANUF("Software Result Enterprises", SW_RESULT_ENTS)
+   MANUF("Masoboshi", MASOBOSHI)
+   MANUF("DelaComp", DELACOMP)
    MANUF("Village Tronic", VILLAGE_TRONIC)
    MANUF("Utilities Unlimited", UTILITIES_ULTD)
+   MANUF("Amitrix", AMITRIX)
    MANUF("MTEC", MTEC)
    MANUF("Great Valley Products", GVP2)
    MANUF("Helfrich", HELFRICH2)
    MANUF("MacroSystems", MACROSYSTEMS)
+   MANUF("ElBox Computer", ELBOX)
    MANUF("Harms Professional", HARMS_PROF)
+   MANUF("Micronik", MICRONIK)
+   MANUF("Imtronics", IMTRONICS2)
+   MANUF("Kupke", KUPKE3)
+   MANUF("Information", INFORMATION)
    MANUF("Vortex", VORTEX)
    MANUF("DataFlyer", DATAFLYER)
+   MANUF("ReadySoft", READYSOFT)
    MANUF("Phase5", PHASE5)
+   MANUF("DPS", DPS)
+   MANUF("Apollo", APOLLO2)
    MANUF("Apollo", APOLLO)
    MANUF("Uwe Gerlach", UWE_GERLACH)
    MANUF("MacroSystems", MACROSYSTEMS2)
+   MANUF("Combitec", COMBITEC)
+   MANUF("SKI Peripherals", SKI)
+   MANUF("Cameron", CAMERON)
+   MANUF("Reis-Ware", REIS_WARE)
 END
 
 #define NUM_MANUF (sizeof(Manufacturers)/sizeof(struct Manufacturer))
@@ -511,7 +757,7 @@
                   break;
                } else {
                   epc = *(enum GVP_ident *)ZTWO_VADDR(addr+0x8000) &
-                        GVP_EPCMASK;
+                        GVP_PRODMASK;
                   for (k = 0; k < NUM_GVP_PROD; k++)
                      if (Ext_Prod_GVP[k].ID == epc) {
                         prodname = Ext_Prod_GVP[k].Name;
diff -urN pc-2.0.21/arch/m68k/atari/Makefile linux-2.0.21/arch/m68k/atari/Makefile
--- pc-2.0.21/arch/m68k/atari/Makefile	Mon May  6 11:44:30 1996
+++ linux-2.0.21/arch/m68k/atari/Makefile	Sat Aug 24 14:29:05 1996
@@ -11,7 +11,6 @@
 
 O_TARGET := atari.o
 O_OBJS	 := config.o atakeyb.o ataints.o \
-            stdma.o atasound.o joystick.o stram.o atafb.o
-OX_OBJS = ksyms.o
+            stdma.o atasound.o joystick.o stram.o atafb.o ksyms.o
 
 include $(TOPDIR)/Rules.make
diff -urN pc-2.0.21/arch/m68k/atari/atafb.c linux-2.0.21/arch/m68k/atari/atafb.c
--- pc-2.0.21/arch/m68k/atari/atafb.c	Mon Jul 15 08:55:11 1996
+++ linux-2.0.21/arch/m68k/atari/atafb.c	Thu Sep  5 19:46:07 1996
@@ -39,6 +39,7 @@
 #define ATAFB_FALCON
 
 #include <linux/kernel.h>
+#include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/mm.h>
@@ -46,13 +47,13 @@
 #include <linux/malloc.h>
 #include <linux/delay.h>
 
+#include <asm/setup.h>
 #include <asm/segment.h>
 #include <asm/pgtable.h>
 #include <asm/irq.h>
 
 #include <asm/atarihw.h>
 #include <asm/atariints.h>
-#include <asm/bootinfo.h>
 
 #include <linux/fb.h>
 #include <asm/atarikb.h>
@@ -83,10 +84,6 @@
 static int ovsc_offset=0, ovsc_addlen=0;
 int        ovsc_switchmode=0;
 
-#ifdef ATAFB_FALCON
-static int pwrsave = 0;	/* use VESA suspend mode instead of blanking only? */
-#endif
-
 static struct atari_fb_par {
 	unsigned long screen_base;
 	int vyres;
@@ -282,9 +279,12 @@
  *   if yres_virtual > yres or xres_virtual > xres.
  *
  * int (*blank)( int blank_mode )
- *   Blank the screen if blank_mode != 0, else unblank. If NULL then blanking
- *   is done by setting the CLUT to black. Return != 0 if un-/blanking
- *   failed due to e.g. video mode which doesn't support it.
+ *   Blank the screen if blank_mode!=0, else unblank. If blank==NULL then
+ *   the caller blanks by setting the CLUT to all black. Return 0 if blanking
+ *   succeeded, !=0 if un-/blanking failed due to e.g. a video mode which
+ *   doesn't support it. Implements VESA suspend and powerdown modes on
+ *   hardware that supports disabling hsync/vsync:
+ *       blank_mode==2: suspend vsync, 3:suspend hsync, 4: powerdown.
  */
 
 static struct fb_hwswitch {
@@ -529,6 +529,7 @@
 	int yres=var->yres;
 	int bpp=var->bits_per_pixel;
 	int linelen;
+	int yres_virtual = var->yres_virtual;
 
 	if (mono_moni) {
 		if (bpp > 1 || xres > sttt_xres*2 || yres >tt_yres*2)
@@ -582,14 +583,23 @@
 			bpp=1;
 		}
 	}
+	if (yres_virtual <= 0)
+		yres_virtual = 0;
+	else if (yres_virtual < yres)
+		yres_virtual = yres;
 	if (var->sync & FB_SYNC_EXT)
 		par->hw.tt.sync=0;
 	else
 		par->hw.tt.sync=1;
 	linelen=xres*bpp/8;
-	if ((var->yoffset + yres)*linelen > screen_len && screen_len)
+	if (yres_virtual * linelen > screen_len && screen_len)
 		return -EINVAL;
-	par->screen_base=screen_base+ var->yoffset*linelen;
+	if (yres * linelen > screen_len && screen_len)
+		return -EINVAL;
+	if (var->yoffset + yres > yres_virtual && yres_virtual)
+		return -EINVAL;
+	par->vyres = yres_virtual;
+	par->screen_base = screen_base + var->yoffset * linelen;
 	return 0;
 }
 
@@ -665,7 +675,11 @@
 	if (! use_hwscroll)
 		var->yres_virtual=var->yres;
 	else if (screen_len)
-		var->yres_virtual=screen_len/linelen;
+		if (par->vyres)
+			var->yres_virtual = par->vyres;
+		else
+			/* vyres==0 means use maximum */
+			var->yres_virtual = screen_len / linelen;
 	else {
 		if (hwscroll < 0)
 			var->yres_virtual = 2 * var->yres;
@@ -784,17 +798,17 @@
 static struct pixel_clock {
 	unsigned long f;	/* f/[Hz] */
 	unsigned long t;	/* t/[ps] (=1/f) */
-	short right, hsync, left;	/* standard timing in clock cycles, not pixel */
+	int right, hsync, left;	/* standard timing in clock cycles, not pixel */
 		/* hsync initialized in falcon_detect() */
-	short sync_mask;	/* or-mask for hw.falcon.sync to set this clock */
-	short control_mask; /* ditto, for hw.falcon.vid_control */
+	int sync_mask;		/* or-mask for hw.falcon.sync to set this clock */
+	int control_mask;	/* ditto, for hw.falcon.vid_control */
 }
-f25  = {25175000, 39722, 18, 0, 42, 0x0, VCO_CLOCK25},
+f25  = {25175000, 39721, 18, 0, 42, 0x0, VCO_CLOCK25},
 f32  = {32000000, 31250, 18, 0, 42, 0x0, 0},
 fext = {       0,     0, 18, 0, 42, 0x1, 0};
 
 /* VIDEL-prescale values [mon_type][pixel_length from VCO] */
-static short vdl_prescale[4][3] = {{4,2,1}, {4,2,1}, {4,2,2}, {4,2,1}};
+static int vdl_prescale[4][3] = {{4,2,1}, {4,2,1}, {4,2,2}, {4,2,1}};
 
 /* Default hsync timing [mon_type] in picoseconds */
 static long h_syncs[4] = {3000000, 4700000, 4000000, 4700000};
@@ -817,22 +831,22 @@
 	fix->type = FB_TYPE_INTERLEAVED_PLANES;
 	fix->type_aux = 2;
 	fix->visual = FB_VISUAL_PSEUDOCOLOR;
+	fix->xpanstep = 1;
+	fix->ypanstep = 1;
+	fix->ywrapstep = 0;
 	if (par->hw.falcon.mono) {
 		fix->type = FB_TYPE_PACKED_PIXELS;
 		fix->type_aux = 0;
+		/* no smooth scrolling with longword aligned video mem */
+		fix->xpanstep = 32;
 	}
 	else if (par->hw.falcon.f_shift & 0x100) {
 		fix->type = FB_TYPE_PACKED_PIXELS;
 		fix->type_aux = 0;
-		fix->visual = FB_VISUAL_TRUECOLOR;  /* is this ok or should this be DIRECTCOLOR? */
+		/* Is this ok or should it be DIRECTCOLOR? */
+		fix->visual = FB_VISUAL_TRUECOLOR;
+		fix->xpanstep = 2;
 	}
-	if (par->hw.falcon.mono)
-		/* no smooth scrolling possible with longword aligned video mem */
-		fix->xpanstep = 32;
-	else
-		fix->xpanstep = 1;
-	fix->ypanstep = 1;
-	fix->ywrapstep = 0;
 	fix->line_length = 0;
 	for (i=0; i<arraysize(fix->reserved); i++)
 		fix->reserved[i]=0;
@@ -843,7 +857,6 @@
 static int falcon_decode_var( struct fb_var_screeninfo *var,
 							  struct atari_fb_par *par )
 {
-	int use_default_timing = 0;
 	int bpp = var->bits_per_pixel;
 	int xres = var->xres;
 	int yres = var->yres;
@@ -970,20 +983,22 @@
 		xres_virtual = (xres_virtual + 31) & ~31;
 	else
 		xres_virtual = (xres_virtual + 15) & ~15;
-	/* <=0 : yres_virtual determined by screensize */
-	if (yres_virtual < yres && yres_virtual > 0)
+
+	if (yres_virtual <= 0)
+		yres_virtual = 0;
+	else if (yres_virtual < yres)
 		yres_virtual = yres;
 
+	/* backward bug-compatibility */
+	if (var->pixclock > 1)
+		var->pixclock -= 1;
+
 	par->hw.falcon.line_width = bpp * xres / 16;
 	par->hw.falcon.line_offset = bpp * (xres_virtual - xres) / 16;
 
 	/* single or double pixel width */
 	xstretch = (xres == 320) ? 2 : 1;
 
-	/* Default values are used for vert./hor. timing if no pixelclock given. */
-	if (var->pixclock == 0)
-		use_default_timing = 1;
-
 #if 0 /* currently unused */
 	if (mon_type == F_MON_SM) {
 		if (xres != 640 && yres != 400)
@@ -1011,7 +1026,8 @@
 	{	/* F_MON_VGA */
 		if (bpp == 16)
 			xstretch = 2; /* hicolor only double pixel width */
-		if (use_default_timing) {
+		/* Default values are used for vert./hor. timing if no pixelclock given. */
+		if (var->pixclock == 0) {
 			int linesize;
 
 			/* Choose master pixelclock depending on hor. timing */
@@ -1035,37 +1051,28 @@
 			vsync_len = 3;
 		}
 		else {
-#if 0 /* TODO enable this (untested yet) */
-			/* Round down pixelclock */
-			int i; unsigned long pcl=0;
-			for (i=1; i<=4; i*=2) {
-				if (f25.t*i<=var->pixclock && pcl<f25.t*i) {
-					pcl=f25.t*i; pclock=&f25;
+			/* Choose largest pixelclock <= wanted clock */
+			int i;
+			unsigned long pcl = ULONG_MAX;
+			pclock = 0;
+			for (i=1; i <= 4; i *= 2) {
+				if (f25.t*i >= var->pixclock && f25.t*i < pcl) {
+					pcl = f25.t * i;
+					pclock = &f25;
 				}
-				if (f32.t*i<=var->pixclock && pcl<f32.t*i) {
-					pcl=f32.t*i; pclock=&f32;
+				if (f32.t*i >= var->pixclock && f32.t*i < pcl) {
+					pcl = f32.t * i;
+					pclock = &f32;
 				}
-				if (fext.t && fext.t*i<=var->pixclock && pcl<fext.t*i) {
-					pcl=fext.t*i; pclock=&fext;
+				if (fext.t && fext.t*i >= var->pixclock && fext.t*i < pcl) {
+					pcl = fext.t * i;
+					pclock = &fext;
 				}
 			}
-			if (!pcl)
+			if (!pclock)
 				return -EINVAL;
 			plen = pcl / pclock->t;
 
-#else
-			if (var->pixclock == f25.t || var->pixclock == 2*f25.t)
-				pclock = &f25;
-			else if (var->pixclock == f32.t || var->pixclock == 2*f32.t)
-				pclock = &f32;
-			else if ((var->pixclock == fext.t || var->pixclock == 2*fext.t) && fext.t) {
-				pclock = &fext;
-			}
-			else
-				return -EINVAL;
-			plen = var->pixclock / pclock->t;
-#endif
-
 			left_margin = var->left_margin;
 			right_margin = var->right_margin;
 			hsync_len = var->hsync_len;
@@ -1122,7 +1129,7 @@
 	/*********************
 	Horizontal timing: unit = [master clock cycles]
 	unit of hxx-registers: [master clock cycles * prescale]
-	Hxx-registers are 9-bit wide
+	Hxx-registers are 9 bit wide
 
 	1 line = ((hht + 2) * 2 * prescale) clock cycles
 
@@ -1209,7 +1216,7 @@
 	 * of the first displayed line!
 	 * One frame consists of VFT+1 half lines. VFT+1 must be even in
 	 * non-interlace, odd in interlace mode for synchronisation.
-	 * Vxx-registers are 11-bit wide
+	 * Vxx-registers are 11 bit wide
 	 */
 	par->VBE = (upper_margin * 2 + 1); /* must begin on odd halfline */
 	par->VDB = par->VBE;
@@ -1264,11 +1271,11 @@
 
   set_screen_base:
 	linelen = xres_virtual * bpp / 8;
-	if ((var->yoffset + yres)*linelen > screen_len && screen_len)
+	if (yres_virtual * linelen > screen_len && screen_len)
 		return -EINVAL;
-	if (var->yres_virtual * linelen > screen_len && screen_len)
+	if (yres * linelen > screen_len && screen_len)
 		return -EINVAL;
-	if (var->yres * linelen > screen_len && screen_len)
+	if (var->yoffset + yres > yres_virtual && yres_virtual)
 		return -EINVAL;
 	par->vyres = yres_virtual;
 	par->screen_base = screen_base + var->yoffset * linelen;
@@ -1368,7 +1375,8 @@
 		if (par->vyres)
 			var->yres_virtual = par->vyres;
 		else
-			var->yres_virtual=screen_len/linelen;
+			/* vyres==0 means use maximum */
+			var->yres_virtual = screen_len / linelen;
 	else {
 		if (hwscroll < 0)
 			var->yres_virtual = 2 * var->yres;
@@ -1499,7 +1507,7 @@
 }
 
 
-static void falcon_vbl_switcher( int irq, struct pt_regs *fp, void *dummy )
+static void falcon_vbl_switcher( int irq, void *dummy, struct pt_regs *fp )
 {
 	struct falcon_hw *hw = &f_new_mode;
 
@@ -1529,8 +1537,6 @@
 		videl.vde = hw->vde;
 		videl.vss = hw->vss;
 
-		/*f030_sreg[2] = 0;*/
-
 		videl.f_shift = 0; /* write enables Falcon palette, 0: 4 planes */
 		if (hw->ste_mode) {
 			videl.st_shift = hw->st_shift; /* write enables STE palette */
@@ -1565,19 +1571,24 @@
 							   struct atari_fb_par *par )
 {
 	int xoffset;
+	int bpp = disp[currcon].var.bits_per_pixel;
 
-	if (disp[currcon].var.bits_per_pixel == 1)
+	if (bpp == 1)
 		var->xoffset = up(var->xoffset, 32);
-	par->hw.falcon.xoffset = var->xoffset & 15;
-	par->hw.falcon.line_offset = disp[currcon].var.bits_per_pixel *
-	       	  (disp[currcon].var.xres_virtual - disp[currcon].var.xres) / 16;
+	if (bpp != 16)
+		par->hw.falcon.xoffset = var->xoffset & 15;
+	else {
+		par->hw.falcon.xoffset = 0;
+		var->xoffset = up(var->xoffset, 2);
+	}
+	par->hw.falcon.line_offset = bpp *
+	       	(disp[currcon].var.xres_virtual - disp[currcon].var.xres) / 16;
 	if (par->hw.falcon.xoffset)
-		par->hw.falcon.line_offset -= disp[currcon].var.bits_per_pixel;
+		par->hw.falcon.line_offset -= bpp;
 	xoffset = var->xoffset - par->hw.falcon.xoffset;
 
-	par->screen_base
-		= screen_base + (var->yoffset * disp[currcon].var.xres_virtual +
-				xoffset) * disp[currcon].var.bits_per_pixel / 8;
+	par->screen_base = screen_base +
+	        (var->yoffset * disp[currcon].var.xres_virtual + xoffset) * bpp / 8;
 	if (fbhw->set_screen_base)
 		fbhw->set_screen_base (par->screen_base);
 	else
@@ -1629,24 +1640,37 @@
 {
 /* ++guenther: we can switch off graphics by changing VDB and VDE,
  * so VIDEL doesn't hog the bus while saving.
- * (this affects usleep()).
+ * (this may affect usleep()).
  */
+	int vdb, vss, hbe, hss;
+
 	if (mon_type == F_MON_SM)	/* this doesn't work on SM124 */
 		return 1;
-	if (blank_mode) {
+
+	vdb = current_par.VDB;
+	vss = current_par.VSS;
+	hbe = current_par.HBE;
+	hss = current_par.HSS;
+
+	if (blank_mode >= 1) {
 		/* disable graphics output (this speeds up the CPU) ... */
-		videl.vdb = current_par.VFT + 1;
+		vdb = current_par.VFT + 1;
 		/* ... and blank all lines */
-		videl.hbe = current_par.HHT + 2;
-		/* VESA suspend mode, switch off HSYNC */
-		if (pwrsave && mon_type == F_MON_VGA)
-			videl.hss = current_par.HHT + 2;
+		hbe = current_par.HHT + 2;
 	}
-	else {
-		videl.vdb = current_par.VDB;
-		videl.hbe = current_par.HBE;
-		videl.hss = current_par.HSS;
+	/* use VESA suspend modes on VGA monitors */
+	if (mon_type == F_MON_VGA) {
+		if (blank_mode == 2 || blank_mode == 4)
+			vss = current_par.VFT + 1;
+		if (blank_mode == 3 || blank_mode == 4)
+			hss = current_par.HHT + 2;
 	}
+
+	videl.vdb = vdb;
+	videl.vss = vss;
+	videl.hbe = hbe;
+	videl.hss = hss;
+
 	return 0;
 }
 
@@ -1713,11 +1737,13 @@
 		fix->type_aux=0;
 		fix->visual=FB_VISUAL_MONO10;
 	}
-	fix->xpanstep = 0;
-	if (ATARIHW_PRESENT(EXTD_SHIFTER))
+	if (ATARIHW_PRESENT(EXTD_SHIFTER)) {
+		fix->xpanstep = 16;
 		fix->ypanstep = 1;
-	else
+	} else {
+		fix->xpanstep = 0;
 		fix->ypanstep = 0;
+	}
 	fix->ywrapstep = 0;
 	fix->line_length = 0;
 	for (i=0; i<arraysize(fix->reserved); i++)
@@ -1733,6 +1759,7 @@
 	int yres=var->yres;
 	int bpp=var->bits_per_pixel;
 	int linelen;
+	int yres_virtual = var->yres_virtual;
 
 	if (mono_moni) {
 		if (bpp > 1 || xres > sttt_xres || yres > st_yres)
@@ -1763,13 +1790,22 @@
 		else
 			return -EINVAL;
 	}
+	if (yres_virtual <= 0)
+		yres_virtual = 0;
+	else if (yres_virtual < yres)
+		yres_virtual = yres;
 	if (var->sync & FB_SYNC_EXT)
 		par->hw.st.sync=(par->hw.st.sync & ~1) | 1;
 	else
 		par->hw.st.sync=(par->hw.st.sync & ~1);
 	linelen=xres*bpp/8;
-	if ((var->yoffset + yres)*linelen > screen_len && screen_len)
+	if (yres_virtual * linelen > screen_len && screen_len)
+		return -EINVAL;
+	if (yres * linelen > screen_len && screen_len)
 		return -EINVAL;
+	if (var->yoffset + yres > yres_virtual && yres_virtual)
+		return -EINVAL;
+	par->vyres = yres_virtual;
 	par->screen_base=screen_base+ var->yoffset*linelen;
 	return 0;
 }
@@ -1827,7 +1863,11 @@
 	if (! use_hwscroll)
 		var->yres_virtual=var->yres;
 	else if (screen_len)
-		var->yres_virtual=screen_len/linelen;
+		if (par->vyres)
+			var->yres_virtual = par->vyres;
+		else
+			/* vyres==0 means use maximum */
+			var->yres_virtual = screen_len / linelen;
 	else {
 		if (hwscroll < 0)
 			var->yres_virtual = 2 * var->yres;
@@ -2238,11 +2278,13 @@
 static int pan_display( struct fb_var_screeninfo *var,
                         struct atari_fb_par *par )
 {
-	if (var->xoffset)
+	if (!ATARIHW_PRESENT(EXTD_SHIFTER) && var->xoffset)
 		return -EINVAL;
-	par->screen_base
-		= screen_base + (var->yoffset * disp[currcon].var.xres_virtual
-						 * disp[currcon].var.bits_per_pixel / 8);
+	else
+		var->xoffset = up(var->xoffset, 16);
+	par->screen_base = screen_base +
+	        (var->yoffset * disp[currcon].var.xres_virtual + var->xoffset)
+	        * disp[currcon].var.bits_per_pixel / 8;
 	if (fbhw->set_screen_base)
 		fbhw->set_screen_base (par->screen_base);
 	else
@@ -2609,18 +2651,21 @@
 static int
 atari_fb_set_var(struct fb_var_screeninfo *var, int con)
 {
-	int err,oldxres,oldyres,oldbpp,oldxres_virtual,oldyoffset;
+	int err,oldxres,oldyres,oldbpp,oldxres_virtual,
+	    oldyres_virtual,oldyoffset;
 	if ((err=do_fb_set_var(var, con==currcon)))
 		return err;
 	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
 		oldxres=disp[con].var.xres;
 		oldyres=disp[con].var.yres;
 		oldxres_virtual=disp[con].var.xres_virtual;
+		oldyres_virtual=disp[con].var.yres_virtual;
 		oldbpp=disp[con].var.bits_per_pixel;
 		oldyoffset=disp[con].var.yoffset;
 		disp[con].var=*var;
 		if (oldxres != var->xres || oldyres != var->yres 
 		    || oldxres_virtual != var->xres_virtual
+		    || oldyres_virtual != var->yres_virtual
 		    || oldbpp != var->bits_per_pixel
 		    || oldyoffset != var->yoffset) {
 			atari_fb_set_disp(con);
@@ -2778,6 +2823,13 @@
 	return 0;
 }
 
+/* (un)blank/poweroff
+ * 0 = unblank
+ * 1 = blank
+ * 2 = suspend vsync
+ * 3 = suspend hsync
+ * 4 = off
+ */
 static void
 atafb_blank(int blank)
 {
@@ -2828,8 +2880,8 @@
 #ifdef ATAFB_FALCON
 		if (ATARIHW_PRESENT(VIDEL_SHIFTER)) {
 			fbhw = &falcon_switch;
-			add_isr(IRQ_AUTO_4, falcon_vbl_switcher, IRQ_TYPE_PRIO, NULL,
-					"framebuffer/modeswitch");
+			request_irq(IRQ_AUTO_4, falcon_vbl_switcher, IRQ_TYPE_PRIO,
+			            "framebuffer/modeswitch", falcon_vbl_switcher);
 			break;
 		}
 #endif
@@ -2862,7 +2914,7 @@
 		real_screen_base=screen_base+ovsc_offset;
 		screen_len = (mem_req - pad - ovsc_offset) & PAGE_MASK;
 		st_ovsc_switch(ovsc_switchmode);
-		if (m68k_is040or060) {
+		if (CPU_IS_040_OR_060) {
 			/* On a '040+, the cache mode of video RAM must be set to
 			 * write-through also for internal video hardware! */
 			cache_push( VTOP(screen_base), screen_len );
@@ -2979,12 +3031,10 @@
 	else if (!strncmp(this_opt,"internal:",9))
 		strcpy(int_str, this_opt+9);
 #ifdef ATAFB_FALCON
-	else if (!strcmp(this_opt, "pwrsave"))
-		pwrsave = 1;
 	else if (!strncmp(this_opt, "eclock:", 7)) {
 		fext.f = simple_strtoul(this_opt+7, NULL, 10);
 		/* external pixelclock in kHz --> ps */
-		fext.t = (2000000000UL/fext.f+1)/2;
+		fext.t = 1000000000/fext.f;
 		fext.f *= 1000;
 	}
 	else if (!strncmp(this_opt, "monitorcap:", 11))
diff -urN pc-2.0.21/arch/m68k/atari/ataints.c linux-2.0.21/arch/m68k/atari/ataints.c
--- pc-2.0.21/arch/m68k/atari/ataints.c	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/atari/ataints.c	Sat Sep 14 15:17:32 1996
@@ -9,9 +9,9 @@
  *  about non-autovec ints yet. It hardcoded the number of possible ints to
  *  7 (IRQ1...IRQ7). But the Atari has lots of non-autovec ints! I made the
  *  number of possible ints a constant defined in interrupt.h, which is
- *  47 for the Atari. So we can call add_isr() for all Atari interrupts just
- *  the normal way. Additionally, all vectors >= 48 are initialized to call
- *  trap() instead of inthandler(). This must be changed here, too.
+ *  47 for the Atari. So we can call request_irq() for all Atari interrupts
+ *  just the normal way. Additionally, all vectors >= 48 are initialized to
+ *  call trap() instead of inthandler(). This must be changed here, too.
  *
  * 1995-07-16 Lars Brinkhoff <f93labr@dd.chalmers.se>:
  *  Corrected a bug in atari_add_isr() which rejected all SCC
@@ -24,6 +24,11 @@
  *  Total rewrite of Atari interrupt handling, for new scheme see comments
  *  below.
  *
+ * 1996-09-03 lars brinkhoff <f93labr@dd.chalmers.se>:
+ *  Added new function atari_unregister_vme_int(), and
+ *  modified atari_register_vme_int() as well as IS_VALID_INTNO()
+ *  to work with it.
+ *
  * 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.
@@ -35,13 +40,13 @@
 #include <linux/ptrace.h>
 #include <linux/kernel_stat.h>
 
+#include <asm/setup.h>
 #include <asm/system.h>
 #include <asm/traps.h>
 
 #include <asm/atarihw.h>
 #include <asm/atariints.h>
 #include <asm/atari_stdma.h>
-#include <asm/bootinfo.h>
 #include <asm/irq.h>
 
 
@@ -74,14 +79,14 @@
  * The feature of more than one handler for one int source is still there, but
  * only applicable if all handers are of the same type. To not slow down
  * processing of ints with only one handler by the chaining feature, the list
- * calling function atari_call_isr_list() is only plugged in at the time the
+ * calling function atari_call_irq_list() is only plugged in at the time the
  * second handler is registered.
  *
  * Implementation notes: For fast-as-possible int handling, there are separate
  * entry points for each type (slow/fast/prio). The assembler handler calls
- * the isr directly in the usual case, no C wrapper is involved. In case of
- * multiple handlers, atari_call_isr_list() is registered as handler and calls
- * in turn the real isr's. To ease access from assembler level to the isr
+ * the irq directly in the usual case, no C wrapper is involved. In case of
+ * multiple handlers, atari_call_irq_list() is registered as handler and calls
+ * in turn the real irq's. To ease access from assembler level to the irq
  * function pointer and accompanying data, these two are stored in a separate
  * array, irq_handler[]. The rest of data (type, name) are put into a second
  * array, irq_param, that is accessed from C only. For each slow interrupt (32
@@ -106,17 +111,17 @@
 typedef void (*asm_irq_handler)(void);
 
 struct irqhandler {
-	isrfunc	isr;
-	void	*data;
+	void	(*handler)(int, void *, struct pt_regs *);
+	void	*dev_id;
 };
 
 struct irqparam {
-	int type;
-	char *name;
+	unsigned long	flags;
+	const char	*devname;
 };
 
 /*
- * Array with isr's and their parameter data. This array is accessed from low
+ * Array with irq's and their parameter data. This array is accessed from low
  * level assembler code, so an element size of 8 allows usage of index scaling
  * addressing mode.
  */
@@ -130,11 +135,11 @@
 static struct irqparam irq_param[NUM_INT_SOURCES];
 
 /*
- * Counter for next free interrupt vector number
+ * Bitmap for free interrupt vector numbers
  * (new vectors starting from 0x70 can be allocated by
  * atari_register_vme_int())
  */
-static int next_free_vme_vec = VME_SOURCE_BASE;
+static int free_vme_vec_bitmap = 0;
 
 /* check for valid int number (complex, sigh...) */
 #define	IS_VALID_INTNO(n)											\
@@ -147,8 +152,9 @@
 	  /* SCC ok if present and number even */						\
 	  ((n) >= SCC_SOURCE_BASE && (n) < VME_SOURCE_BASE &&			\
 	   !((n) & 1) && ATARIHW_PRESENT(SCC)) ||						\
-	  /* greater numbers ok if less than #registered VME vectors */	\
-	  ((n) >= VME_SOURCE_BASE && (n) < next_free_vme_vec)))
+	  /* greater numbers ok if they are registered VME vectors */		\
+	  ((n) >= VME_SOURCE_BASE && (n) < VME_SOURCE_BASE + VME_MAX_SOURCES && \
+		  free_vme_vec_bitmap & (1 << ((n) - VME_SOURCE_BASE)))))
 
 
 /*
@@ -165,8 +171,7 @@
 #define SR "0x28"
 #define SAVE_ALL				\
 	"clrl	%%sp@-;"    /* stk_adj */	\
-	"clrl	%%sp@-;"			\
-	"subql	#1,%%sp@;"  /* orig d0 = -1 */	\
+	"pea	-1:w;"	    /* orig d0 = -1 */	\
 	"movel	%%d0,%%sp@-;" /* d0 */		\
 	"moveml	%%d1-%%d5/%%a0-%%a1,%%sp@-"
 
@@ -176,21 +181,19 @@
 void atari_slow_irq_##n##_dummy (void) {				   \
 __asm__ (ALIGN_STR "\n"							   \
 SYMBOL_NAME_STR(atari_slow_irq_) #n "_handler:\t"			   \
-	SAVE_ALL "\n"							   \
 "	addql	#1,"SYMBOL_NAME_STR(intr_count)"\n"			   \
+	SAVE_ALL "\n"							   \
 "	andb	#~(1<<(" #n "&7)),"	/* mask this interrupt */	   \
 	"("MFP_MK_BASE"+(((" #n "&8)^8)>>2)+((" #n "&16)<<3)):w\n"	   \
-"	movew	%%sp@("SR"),%%d0\n"	/* get old IPL from stack frame */ \
+"	bfextu	%%sp@("SR"){#5,#3},%%d0\n" /* get old IPL from stack frame */ \
 "	movew	%%sr,%%d1\n"						   \
-"	andw	#0x0700,%%d0\n"						   \
-"	andw	#0xf8ff,%%d1\n"						   \
-"	orw 	%%d0,%%d1\n"						   \
+"	bfins	%%d0,%%d1{#21,#3}\n"					   \
 "	movew	%%d1,%%sr\n"		/* set IPL = previous value */	   \
 "	addql	#1,%a0\n"						   \
 "	lea	"SYMBOL_NAME_STR(irq_handler)"+("#n"+8)*8,%%a0\n"	   \
+"	pea 	%%sp@\n"		/* push addr of frame */	   \
 "	movel	%%a0@(4),%%sp@-\n"	/* push handler data */		   \
-"	pea 	%%sp@(4)\n"		/* push addr of frame */	   \
-"	pea 	(" #n "+8):w\n"		/* push int number */		   \
+"	pea 	(" #n "+0x10000008)\n"	/* push int number */		   \
 "	movel	%%a0@,%%a0\n"						   \
 "	jbsr	%%a0@\n"		/* call the handler */		   \
 "	addql	#8,%%sp\n"						   \
@@ -280,21 +283,21 @@
 __asm__ (ALIGN_STR "\n"
 SYMBOL_NAME_STR(atari_fast_irq_handler) ":
 	orw 	#0x700,%%sr		/* disable all interrupts */
-"SYMBOL_NAME_STR(atari_prio_irq_handler) ":\t"
+"SYMBOL_NAME_STR(atari_prio_irq_handler) ":\t
+	addql	#1,"SYMBOL_NAME_STR(intr_count)"\n"
 	SAVE_ALL "
-	addql	#1,"SYMBOL_NAME_STR(intr_count)"
-	movew	%%sp@(" FORMATVEC "),%%d0	/* get vector number from stack frame */
-	andil	#0xfff,%%d0		/* mask off format nibble */
-	lsrl	#2,%%d0			/* convert vector to source */
-	subl	#(0x40-8),%%d0
+	/* get vector number from stack frame and convert to source */
+	bfextu	%%sp@(" FORMATVEC "){#4,#10},%%d0
+	subw	#(0x40-8),%%d0
 	jpl 	1f
-	addl	#(0x40-8-0x18),%%d0
+	addw	#(0x40-8-0x18),%%d0
 1:	lea	%a0,%%a0
 	addql	#1,%%a0@(%%d0:l:4)
 	lea	"SYMBOL_NAME_STR(irq_handler)",%%a0
 	lea	%%a0@(%%d0:l:8),%%a0
+	pea 	%%sp@			/* push frame address */
 	movel	%%a0@(4),%%sp@-		/* push handler data */
-	pea 	%%sp@(4)		/* push frame address */
+	bset	#28,%%d0		/* set MACHSPEC bit */
 	movel	%%d0,%%sp@-		/* push int number */
 	movel	%%a0@,%%a0
 	jsr	%%a0@			/* and call the handler */
@@ -313,12 +316,7 @@
 asm(".text\n"
 ALIGN_STR "\n"
 SYMBOL_NAME_STR(falcon_hblhandler) ":
-	movel	%d0,%sp@-
-	movew	%sp@(4),%d0
-	andw	#0xf8ff,%d0
-	orw	#0x0200,%d0	/* set saved ipl to 2 */
-	movew	%d0,%sp@(4)
-	movel	%sp@+,%d0
+	orw	#0x200,%sp@	/* set saved ipl to 2 */
 	rte");
 
 /* Defined in entry.S; only increments 'num_spurious' */
@@ -327,7 +325,7 @@
 extern void atari_microwire_cmd( int cmd );
 
 /*
- * void atari_init_INTS (void)
+ * void atari_init_IRQ (void)
  *
  * Parameters:	None
  *
@@ -337,7 +335,7 @@
  * the atari IRQ handling routines.
  */
 
-void atari_init_INTS(void)
+void atari_init_IRQ(void)
 {
 	int i;
 
@@ -353,9 +351,9 @@
 #else
 	mfp.vec_adr  = 0x40;	/* Automatic EOI-Mode */
 #endif
-	mfp.int_en_a =		    /* turn off MFP-Ints */
+	mfp.int_en_a = 0x00;	/* turn off MFP-Ints */
 	mfp.int_en_b = 0x00;
-	mfp.int_mk_a =			/* no Masking */
+	mfp.int_mk_a = 0xff;	/* no Masking */
 	mfp.int_mk_b = 0xff;
 
 	if (ATARIHW_PRESENT(TT_MFP)) {
@@ -364,9 +362,9 @@
 #else
 		tt_mfp.vec_adr  = 0x50;		/* Automatic EOI-Mode */
 #endif
-		tt_mfp.int_en_a =			/* turn off MFP-Ints */
+		tt_mfp.int_en_a = 0x00;		/* turn off MFP-Ints */
 		tt_mfp.int_en_b = 0x00;
-		tt_mfp.int_mk_a =			/* no Masking */
+		tt_mfp.int_mk_a = 0xff;		/* no Masking */
 		tt_mfp.int_mk_b = 0xff;
 	}
 
@@ -407,177 +405,169 @@
 }
 
 
-static void atari_call_isr_list( int irq, struct pt_regs *fp, void *_p )
+static void atari_call_irq_list( int irq, void *dev_id, struct pt_regs *fp )
 {
-  isr_node_t *p;
-	
-  for( p = (isr_node_t *)_p; p; p = p->next )
-    p->isr( irq, fp, p->data );
+	irq_node_t *node;
+
+	for (node = (irq_node_t *)dev_id; node; node = node->next)
+		node->handler(irq, node->dev_id, fp);
 }
 
 
 /*
- * atari_add_isr : add an interrupt service routine for a particular
- *		   machine specific interrupt source.
- *		   If the addition was successful, it returns 1, otherwise
- *		   it returns 0.  It will fail if the interrupt is already
- *                 occupied of another handler with different type
+ * atari_request_irq : add an interrupt service routine for a particular
+ *                     machine specific interrupt source.
+ *                     If the addition was successful, it returns 0.
  */
 
-int atari_add_isr(unsigned long source, isrfunc isr, int type, void
-		  *data, char *name)
+int atari_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
+                      unsigned long flags, const char *devname, void *dev_id)
 {
 	int vector;
 	
-	source &= ~IRQ_MACHSPEC;
-	if (type < IRQ_TYPE_SLOW || type > IRQ_TYPE_PRIO) {
-		printk ("atari_add_isr: Bad irq type %d requested from %s\n",
-				type, name );
-		return( 0 );
-	}
-	if (!IS_VALID_INTNO(source)) {
-		printk ("atari_add_isr: Unknown irq %ld requested from %s\n",
-				source, name );
-		return( 0 );
+	if (flags < IRQ_TYPE_SLOW || flags > IRQ_TYPE_PRIO) {
+		printk ("%s: Bad irq type %ld requested from %s\n",
+		        __FUNCTION__, flags, devname);
+		return -EINVAL;
+	}
+	if (!IS_VALID_INTNO(irq)) {
+		printk ("%s: Unknown irq %d requested from %s\n",
+		        __FUNCTION__, irq, devname);
+		return -ENXIO;
 	}
-	vector = IRQ_SOURCE_TO_VECTOR(source);
+	vector = IRQ_SOURCE_TO_VECTOR(irq);
 
 	/*
 	 * Check type/source combination: slow ints are (currently)
 	 * only possible for MFP-interrupts.
 	 */
-	if (type == IRQ_TYPE_SLOW &&
-		(source < STMFP_SOURCE_BASE || source >= SCC_SOURCE_BASE)) {
-		printk ("atari_add_isr: Slow irq requested for non-MFP source %ld from %s\n",
-				source, name );
-		return( 0 );
+	if (flags == IRQ_TYPE_SLOW &&
+		(irq < STMFP_SOURCE_BASE || irq >= SCC_SOURCE_BASE)) {
+		printk ("%s: Slow irq requested for non-MFP source %d from %s\n",
+		        __FUNCTION__, irq, devname);
+		return -EINVAL;
 	}
 		
 	if (vectors[vector] == bad_interrupt) {
 		/* int has no handler yet */
-		irq_handler[source].isr = isr;
-		irq_handler[source].data = data;
-		irq_param[source].type = type;
-		irq_param[source].name = name;
+		irq_handler[irq].handler = handler;
+		irq_handler[irq].dev_id  = dev_id;
+		irq_param[irq].flags   = flags;
+		irq_param[irq].devname = devname;
 		vectors[vector] =
-			(type == IRQ_TYPE_SLOW) ? slow_handlers[source-STMFP_SOURCE_BASE] :
-			(type == IRQ_TYPE_FAST) ? atari_fast_irq_handler :
-									  atari_prio_irq_handler;
+			(flags == IRQ_TYPE_SLOW) ? slow_handlers[irq-STMFP_SOURCE_BASE] :
+			(flags == IRQ_TYPE_FAST) ? atari_fast_irq_handler :
+			                          atari_prio_irq_handler;
 		/* If MFP int, also enable and umask it */
-		atari_turnon_irq(source);
-		atari_enable_irq(source);
+		atari_turnon_irq(irq);
+		atari_enable_irq(irq);
 
-		return 1;
+		return 0;
 	}
-	else if (irq_param[source].type == type) {
+	else if (irq_param[irq].flags == flags) {
 		/* old handler is of same type -> handlers can be chained */
-		isr_node_t *p;
+		irq_node_t *node;
 		unsigned long flags;
 
 		save_flags(flags);
 		cli();
 
-		if (irq_handler[source].isr != atari_call_isr_list) {
+		if (irq_handler[irq].handler != atari_call_irq_list) {
 			/* Only one handler yet, make a node for this first one */
-			p = new_isr_node();
-			if (p == NULL) return 0;
-			p->isr = irq_handler[source].isr;
-			p->data = irq_handler[source].data;
-			p->name = irq_param[source].name;
-			p->next = NULL;
-
-			irq_handler[source].isr = atari_call_isr_list;
-			irq_handler[source].data = p;
-			irq_param[source].name = "chained";
+			if (!(node = new_irq_node()))
+				return -ENOMEM;
+			node->handler = irq_handler[irq].handler;
+			node->dev_id  = irq_handler[irq].dev_id;
+			node->devname = irq_param[irq].devname;
+			node->next = NULL;
+
+			irq_handler[irq].handler = atari_call_irq_list;
+			irq_handler[irq].dev_id  = node;
+			irq_param[irq].devname   = "chained";
 		}
 
-		p = new_isr_node();
-		if (p == NULL) return 0;
-		p->isr = isr;
-		p->data = data;
-		p->name = name;
+		if (!(node = new_irq_node()))
+			return -ENOMEM;
+		node->handler = handler;
+		node->dev_id  = dev_id;
+		node->devname = devname;
 		/* new handlers are put in front of the queue */
-		p->next = irq_handler[source].data;
-		irq_handler[source].data = p;
+		node->next = irq_handler[irq].dev_id;
+		irq_handler[irq].dev_id = node;
 
 		restore_flags(flags);
-		return 1;
-	}
-	else {
-		printk ("atari_add_isr: Irq %ld allocated by other type int (call from %s)\n",
-				source, name );
-		return( 0 );
+		return 0;
+	} else {
+		printk ("%s: Irq %d allocated by other type int (call from %s)\n",
+		        __FUNCTION__, irq, devname);
+		return -EBUSY;
 	}
 }
 
-
-int atari_remove_isr(unsigned long source, isrfunc isr, void *data)
+void atari_free_irq(unsigned int irq, void *dev_id)
 {
 	unsigned long flags;
 	int vector;
-	isr_node_t **p, *q;
-
-	source &= ~IRQ_MACHSPEC;
+	irq_node_t **list, *node;
 
-	if (!IS_VALID_INTNO(source)) {
-		printk("atari_remove_isr: Unknown irq %ld\n", source);
-		return 0;
+	if (!IS_VALID_INTNO(irq)) {
+		printk("%s: Unknown irq %d\n", __FUNCTION__, irq);
+		return;
 	}
 
-	vector = IRQ_SOURCE_TO_VECTOR(source);
+	vector = IRQ_SOURCE_TO_VECTOR(irq);
 	if (vectors[vector] == bad_interrupt)
 		goto not_found;
 
 	save_flags(flags);
 	cli();
 
-	if (irq_handler[source].isr != atari_call_isr_list) {
+	if (irq_handler[irq].handler != atari_call_irq_list) {
 		/* It's the only handler for the interrupt */
-		if (irq_handler[source].isr != isr &&
-		    irq_handler[source].data != data) {
+		if (irq_handler[irq].dev_id != dev_id) {
 			restore_flags(flags);
 			goto not_found;
 		}
-		irq_handler[source].isr = NULL;
-		irq_handler[source].data = NULL;
-		irq_param[source].name = NULL;
+		irq_handler[irq].handler = NULL;
+		irq_handler[irq].dev_id  = NULL;
+		irq_param[irq].devname   = NULL;
 		vectors[vector] = bad_interrupt;
 		/* If MFP int, also disable it */
-		atari_disable_irq(source);
-		atari_turnoff_irq(source);
+		atari_disable_irq(irq);
+		atari_turnoff_irq(irq);
 
 		restore_flags(flags);
-		return 1;
+		return;
 	}
 
-	/* The interrupt is chained, find the isr on the list */
-	for( p = (isr_node_t **)&irq_handler[source].data; *p; p = &(*p)->next ) {
-		if ((*p)->isr == isr) break;
+	/* The interrupt is chained, find the irq on the list */
+	for(list = (irq_node_t **)&irq_handler[irq].dev_id; *list; list = &(*list)->next) {
+		if ((*list)->dev_id == dev_id) break;
 	}
-	if (!*p) {
+	if (!*list) {
 		restore_flags(flags);
 		goto not_found;
 	}
 
-	(*p)->isr = NULL; /* Mark it as free for reallocation */
-	*p = (*p)->next;
+	(*list)->handler = NULL; /* Mark it as free for reallocation */
+	*list = (*list)->next;
 
 	/* If there's now only one handler, unchain the interrupt, i.e. plug in
-	 * the handler directly again and omit atari_call_isr_list */
-	q = (isr_node_t *)irq_handler[source].data;
-	if (q && !q->next) {
-		irq_handler[source].isr = q->isr;
-		irq_handler[source].data = q->data;
-		irq_param[source].name = q->name;
-		q->isr = NULL; /* Mark it as free for reallocation */
+	 * the handler directly again and omit atari_call_irq_list */
+	node = (irq_node_t *)irq_handler[irq].dev_id;
+	if (node && !node->next) {
+		irq_handler[irq].handler = node->handler;
+		irq_handler[irq].dev_id  = node->dev_id;
+		irq_param[irq].devname   = node->devname;
+		node->handler = NULL; /* Mark it as free for reallocation */
 	}
 
 	restore_flags(flags);
-	return 1;
+	return;
 
-  not_found:
-	printk("atari_remove_isr: isr %p not found on list!\n", isr);
-	return 0;
+not_found:
+	printk("%s: tried to remove invalid irq\n", __FUNCTION__);
+	return;
 }
 
 
@@ -588,46 +578,60 @@
 
 unsigned long atari_register_vme_int(void)
 {
-	unsigned long source;
+	int i;
+
+	for(i = 0; i < 32; i++)
+		if((free_vme_vec_bitmap & (1 << i)) == 0)
+			break;
 	
-	if (next_free_vme_vec == NUM_ATARI_SOURCES)
+	if(i == 16)
 		return 0;
 
-	source = next_free_vme_vec | IRQ_MACHSPEC;
-	next_free_vme_vec++;
-	return source;
+	free_vme_vec_bitmap |= 1 << i;
+	return (VME_SOURCE_BASE + i) | IRQ_MACHSPEC;
 }
 
 
-int atari_get_irq_list(char *buf, int len)
+void atari_unregister_vme_int(unsigned long irq)
 {
-	int i;
+	irq &= ~IRQ_MACHSPEC;
+	
+	if(irq >= VME_SOURCE_BASE && irq < VME_SOURCE_BASE + VME_MAX_SOURCES) {
+		irq -= VME_SOURCE_BASE;
+		free_vme_vec_bitmap &= ~(1 << irq);
+	}
+}
+
+
+int atari_get_irq_list(char *buf)
+{
+	int i, len = 0;
 
 	for (i = 0; i < NUM_INT_SOURCES; ++i) {
 		if (vectors[IRQ_SOURCE_TO_VECTOR(i)] == bad_interrupt)
 			continue;
 		if (i < STMFP_SOURCE_BASE)
-			len += sprintf(buf+len, "auto %2d: %8d ",
+			len += sprintf(buf+len, "auto %2d: %10u ",
 				       i, kstat.interrupts[i]);
 		else
-			len += sprintf(buf+len, "vec $%02x: %8d ",
+			len += sprintf(buf+len, "vec $%02x: %10u ",
 				       IRQ_SOURCE_TO_VECTOR(i),
 				       kstat.interrupts[i]);
 
-		if (irq_handler[i].isr != atari_call_isr_list) {
-			len += sprintf(buf+len, "%s\n", irq_param[i].name);
+		if (irq_handler[i].handler != atari_call_irq_list) {
+			len += sprintf(buf+len, "%s\n", irq_param[i].devname);
 		}
 		else {
-			isr_node_t *p;
-			for( p = (isr_node_t *)irq_handler[i].data; p; p = p->next ) {
-				len += sprintf(buf+len, "%s\n", p->name);
+			irq_node_t *p;
+			for( p = (irq_node_t *)irq_handler[i].dev_id; p; p = p->next ) {
+				len += sprintf(buf+len, "%s\n", p->devname);
 				if (p->next)
 					len += sprintf( buf+len, "                  " );
 			}
 		}
 	}
 	if (num_spurious)
-		len += sprintf(buf+len, "spurio.: %8ld\n", num_spurious);
+		len += sprintf(buf+len, "spurio.: %10u\n", num_spurious);
 	
 	return len;
 }
diff -urN pc-2.0.21/arch/m68k/atari/atakeyb.c linux-2.0.21/arch/m68k/atari/atakeyb.c
--- pc-2.0.21/arch/m68k/atari/atakeyb.c	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/atari/atakeyb.c	Sat Aug 24 14:29:09 1996
@@ -29,10 +29,9 @@
 #include <asm/atari_joystick.h>
 #include <asm/irq.h>
 
-extern int do_poke_blanked_console;
-extern void process_keycode (int);
+extern void handle_scancode(unsigned char);
 extern int ovsc_switchmode;
-unsigned char mach_keyboard_type;
+extern unsigned char mach_keyboard_type;
 static void atakeyb_rep( unsigned long ignore );
 extern unsigned int keymap_count;
 
@@ -41,7 +40,6 @@
 /* Hook for mouse driver */
 void (*atari_mouse_interrupt_hook) (char *);
 
-#define ATAKEY_CAPS	(58)
 #define BREAK_MASK	(0x80)
 
 /*
@@ -83,6 +81,11 @@
  *      Alt + Down       -> Scroll forward console (if implemented)
  *      Alt + CapsLock   -> NumLock
  *
+ * ++Andreas:
+ *
+ *  - Help mapped to K_HELP
+ *  - Undo mapped to K_UNDO (= K_F246)
+ *  - Keypad Left/Right Parenthesis mapped to new K_PPAREN[LR]
  */
 
 static u_short ataplain_map[NR_KEYS] = {
@@ -98,7 +101,7 @@
 	0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
 	0xf600, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf200, 0xf121, 0xf11b, 0xf028, 0xf029, 0xf30d, 0xf30c, 0xf307,
+	0xf200, 0xf1ff, 0xf11b, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
 	0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
 	0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
@@ -117,7 +120,7 @@
 	0xf118, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
 	0xf119, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf200, 0xf205, 0xf203, 0xf028, 0xf029, 0xf30d, 0xf30c, 0xf307,
+	0xf200, 0xf205, 0xf203, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
 	0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
 	0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
@@ -136,7 +139,7 @@
 	0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
 	0xf600, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf200, 0xf121, 0xf202, 0xf028, 0xf029, 0xf30d, 0xf30c, 0xf307,
+	0xf200, 0xf1ff, 0xf202, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
 	0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
 	0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
@@ -155,7 +158,7 @@
 	0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
 	0xf600, 0xf200, 0xf115, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf200, 0xf200, 0xf200, 0xf028, 0xf029, 0xf30d, 0xf30c, 0xf307,
+	0xf200, 0xf200, 0xf200, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
 	0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
 	0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
@@ -174,7 +177,7 @@
 	0xf20b, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
 	0xf20a, 0xf200, 0xf209, 0xf87f, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf200, 0xf206, 0xf204, 0xf028, 0xf029, 0xf30d, 0xf30c, 0xf907,
+	0xf200, 0xf206, 0xf204, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf907,
 	0xf908, 0xf909, 0xf904, 0xf905, 0xf906, 0xf901, 0xf902, 0xf903,
 	0xf900, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
@@ -193,7 +196,7 @@
 	0xf118, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
 	0xf119, 0xf200, 0xf115, 0xf87f, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf200, 0xf200, 0xf200, 0xf028, 0xf029, 0xf30d, 0xf30c, 0xf307,
+	0xf200, 0xf200, 0xf200, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
 	0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
 	0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
@@ -212,7 +215,7 @@
 	0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
 	0xf600, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf200, 0xf121, 0xf202, 0xf028, 0xf029, 0xf30d, 0xf30c, 0xf307,
+	0xf200, 0xf1ff, 0xf202, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
 	0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
 	0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
@@ -231,7 +234,7 @@
 	0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
 	0xf600, 0xf200, 0xf115, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
-	0xf200, 0xf200, 0xf200, 0xf028, 0xf029, 0xf30d, 0xf30c, 0xf307,
+	0xf200, 0xf200, 0xf200, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
 	0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
 	0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 	0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
@@ -270,7 +273,7 @@
 {
 	pt_regs = NULL;
 
-	/* Disable keyboard it for the time we call process_keycode(), else a race
+	/* Disable keyboard for the time we call handle_scancode(), else a race
 	 * in the keyboard tty queue may happen */
 	atari_disable_irq( IRQ_MFP_ACIA );
 	del_timer( &atakeyb_rep_timer );
@@ -282,7 +285,7 @@
 		atakeyb_rep_timer.prev = atakeyb_rep_timer.next = NULL;
 		add_timer( &atakeyb_rep_timer );
 
-		process_keycode (rep_scancode);
+		handle_scancode(rep_scancode);
 	}
 
 	atari_enable_irq( IRQ_MFP_ACIA );
@@ -306,7 +309,7 @@
  * because then the keyboard repeat strikes...
  */
 
-static void keyboard_interrupt(int irq, struct pt_regs *fp, void *dummy)
+static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp)
 {
   u_char acia_stat;
   int scancode;
@@ -335,7 +338,7 @@
 	rep_scancode = 0;
 	if (IS_SYNC_CODE(scancode)) {
 	    /* This code seem already to be the start of a new packet or a
-	     * single keycode */
+	     * single scancode */
 	    kb_state.state = KEYBOARD;
 	    goto interpret_scancode;
 	}
@@ -350,6 +353,7 @@
     if (acia_stat & ACIA_RDRF)	/* received a character */
     {
 	scancode = acia.key_data;	/* get it or reset the ACIA, I'll get it! */
+	mark_bh(KEYBOARD_BH);
       interpret_scancode:
 	switch (kb_state.state)
 	{
@@ -398,11 +402,7 @@
 		    add_timer( &atakeyb_rep_timer );
 		}
 
-		process_keycode( break_flag | scancode );
-		do_poke_blanked_console = 1;
-		mark_bh(CONSOLE_BH);
-		add_keyboard_randomness(scancode);
-
+		handle_scancode(break_flag | scancode);
 		break;
 	    }
 	    break;
@@ -482,7 +482,7 @@
 	printk("Error in keyboard communication\n");
     }
 
-    /* process_keycode() can take a lot of time, so check again if
+    /* handle_scancode() can take a lot of time, so check again if
 	 * some character arrived
 	 */
     goto repeat;
@@ -775,8 +775,8 @@
     kb_state.state = KEYBOARD;
     kb_state.len = 0;
 
-    add_isr(IRQ_MFP_ACIA, keyboard_interrupt, IRQ_TYPE_SLOW, NULL,
-	    "keyboard/mouse/MIDI");
+    request_irq(IRQ_MFP_ACIA, keyboard_interrupt, IRQ_TYPE_SLOW,
+                "keyboard/mouse/MIDI", keyboard_interrupt);
 
     atari_turnoff_irq(IRQ_MFP_ACIA);
     do {
diff -urN pc-2.0.21/arch/m68k/atari/atari.mup linux-2.0.21/arch/m68k/atari/atari.mup
--- pc-2.0.21/arch/m68k/atari/atari.mup	Wed Dec 27 21:44:02 1995
+++ linux-2.0.21/arch/m68k/atari/atari.mup	Thu Jan  1 01:00:00 1970
@@ -1,16 +0,0 @@
-gcc -D__KERNEL__ -O2 -m68030 -c atacon.c -o atacon.o
-gcc -D__KERNEL__ -O2 -m68030 -c atasound.c -o atasound.o
-gcc -D__KERNEL__ -O2 -m68030 -c ataints.c -o ataints.o
-gcc -D__KERNEL__ -O2 -m68030 -c atapart.c -o atapart.o
-gcc -D__KERNEL__ -O2 -m68030 -c atakeyb.c -o atakeyb.o
-gcc -D__KERNEL__ -O2 -m68030 -c joystick.c -o joystick.o
-gcc -D__KERNEL__ -O2 -m68030 -c mouse.c -o mouse.o
-gcc -D__KERNEL__ -O2 -m68030 -c config.c -o config.o
-gcc -D__KERNEL__ -O2 -m68030 -c font_8x16.c -o font_8x16.o
-gcc -D__KERNEL__ -O2 -m68030 -c font_8x8.c -o font_8x8.o
-gcc -D__KERNEL__ -O2 -m68030 -c stdma.c -o stdma.o
-
-
-lnx-ld -r -o atari.o atacon.o atasound.o ataints.o atapart.o atakeyb.o config.o font_8x8.o font_8x1.o joystick.o mouse.o stdma.o
-
-cp atari.o ..\makedir
diff -urN pc-2.0.21/arch/m68k/atari/atasound.c linux-2.0.21/arch/m68k/atari/atasound.c
--- pc-2.0.21/arch/m68k/atari/atasound.c	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/atari/atasound.c	Sat Aug 24 14:29:10 1996
@@ -17,17 +17,16 @@
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/major.h>
-#include <linux/config.h>
 #include <linux/fcntl.h>
 #include <linux/errno.h>
 #include <linux/mm.h>
 
+#include <asm/setup.h>
 #include <asm/atarihw.h>
 #include <asm/system.h>
 #include <asm/irq.h>
 #include <asm/pgtable.h>
 #include <asm/atariints.h>
-#include <asm/bootinfo.h>
 
 
 /*
@@ -119,10 +118,10 @@
 	}
 	else {
 
-		/* convert from PC counter value (base frequency 1.193 MHz)
+		/* convert from frequency value
 		 * to PSG period value (base frequency 125 kHz).
 		 */
-		int period = (PSG_FREQ * count + PC_FREQ/2) / PC_FREQ;
+		int period = PSG_FREQ / count;
 
 		if (period > 0xfff) period = 0xfff;
 
diff -urN pc-2.0.21/arch/m68k/atari/config.c linux-2.0.21/arch/m68k/atari/config.c
--- pc-2.0.21/arch/m68k/atari/config.c	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/atari/config.c	Sat Aug 24 14:29:11 1996
@@ -1,7 +1,7 @@
 /*
- *  linux/atari/config.c
+ *  linux/arch/m68k/atari/config.c
  *
- *  Copyright (C) 1994 Bj”rn Brauel
+ *  Copyright (C) 1994 Bjoern Brauel
  *
  *  5/2/94 Roman Hodek:
  *    Added setting of time_adj to get a better clock.
@@ -27,12 +27,13 @@
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/mm.h>
-#include <asm/bootinfo.h>
 #include <linux/mc146818rtc.h>
 #include <linux/kd.h>
 #include <linux/tty.h>
 #include <linux/console.h>
+#include <linux/interrupt.h>
 
+#include <asm/setup.h>
 #include <asm/atarihw.h>
 #include <asm/atarihdreg.h>
 #include <asm/atariints.h>
@@ -43,16 +44,20 @@
 #include <asm/pgtable.h>
 #include <asm/machdep.h>
 
-extern void atari_sched_init(isrfunc);
+extern void atari_sched_init(void (*)(int, void *, struct pt_regs *));
+/* atari specific keyboard functions */
 extern int atari_keyb_init(void);
 extern int atari_kbdrate (struct kbd_repeat *);
 extern void atari_kbd_leds (unsigned int);
-extern void atari_init_INTS (void);
-extern int atari_add_isr (unsigned long, isrfunc, int, void *, char *);
-extern int atari_remove_isr (unsigned long, isrfunc, void *);
-extern void atari_enable_irq (unsigned);
-extern void atari_disable_irq (unsigned);
-extern int atari_get_irq_list (char *buf, int len);
+/* atari specific irq functions */
+extern void atari_init_IRQ (void);
+extern int atari_request_irq (unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
+                              unsigned long flags, const char *devname, void *dev_id);
+extern int atari_free_irq (unsigned int irq, void *dev_id);
+extern void atari_enable_irq (unsigned int);
+extern void atari_disable_irq (unsigned int);
+extern int atari_get_irq_list (char *buf);
+/* atari specific timer functions */
 extern unsigned long atari_gettimeoffset (void);
 extern void atari_mste_gettod (int *, int *, int *, int *, int *, int *);
 extern void atari_gettod (int *, int *, int *, int *, int *, int *);
@@ -206,9 +211,9 @@
     mach_keyb_init       = atari_keyb_init;
     mach_kbdrate         = atari_kbdrate;
     mach_kbd_leds        = atari_kbd_leds;
-    mach_init_INTS       = atari_init_INTS;
-    mach_add_isr         = atari_add_isr;
-    mach_remove_isr      = atari_remove_isr;
+    mach_init_IRQ        = atari_init_IRQ;
+    mach_request_irq     = atari_request_irq;
+    mach_free_irq        = atari_free_irq;
     mach_enable_irq      = atari_enable_irq;
     mach_disable_irq     = atari_disable_irq;
     mach_get_irq_list	 = atari_get_irq_list;
@@ -380,7 +385,7 @@
     }
     printk("\n");
 
-    if (m68k_is040or060)
+    if (CPU_IS_040_OR_060)
         /* Now it seems to be safe to turn of the tt0 transparent
          * translation (the one that must not be turned off in
          * head.S...)
@@ -406,7 +411,7 @@
      * design of the bus.
      */
 
-    if (!m68k_is040or060) {
+    if (CPU_IS_020_OR_030) {
         unsigned long	tt1_val;
         tt1_val = 0xfe008543;	/* Translate 0xfexxxxxx, enable, cache
                                  * inhibit, read and write, FDC mask = 3,
@@ -427,14 +432,15 @@
     }
 }
 
-void atari_sched_init (isrfunc timer_routine)
+void atari_sched_init(void (*timer_routine)(int, void *, struct pt_regs *))
 {
     /* set Timer C data Register */
     mfp.tim_dt_c = INT_TICKS;
     /* start timer C, div = 1:100 */
     mfp.tim_ct_cd = (mfp.tim_ct_cd & 15) | 0x60; 
     /* install interrupt service routine for MFP Timer C */
-    add_isr (IRQ_MFP_TIMC, timer_routine, IRQ_TYPE_SLOW, NULL, "timer");
+    request_irq(IRQ_MFP_TIMC, timer_routine, IRQ_TYPE_SLOW,
+                "timer", timer_routine);
 }
 
 /* ++andreas: gettimeoffset fixed to check for pending interrupt */
@@ -1031,9 +1037,9 @@
 	 "movec	%/d0,%/vbr"
 	 : : : "d0" );
     
-    if (m68k_is040or060) {
+    if (CPU_IS_040_OR_060) {
         unsigned long jmp_addr040 = VTOP(&&jmp_addr_label040);
-	if (m68k_is040or060 == 6) {
+	if (CPU_IS_060) {
 	    /* 68060: clear PCR to turn off superscalar operation */
 	    __asm__ __volatile__
 		("moveq	#0,%/d0\n\t"
diff -urN pc-2.0.21/arch/m68k/atari/ksyms.c linux-2.0.21/arch/m68k/atari/ksyms.c
--- pc-2.0.21/arch/m68k/atari/ksyms.c	Thu May 16 08:05:10 1996
+++ linux-2.0.21/arch/m68k/atari/ksyms.c	Sun Sep  8 16:31:11 1996
@@ -1,4 +1,3 @@
-#include <linux/config.h>
 #include <linux/module.h>
 #include <asm/ptrace.h>
 #include <asm/traps.h>
@@ -13,6 +12,7 @@
 
 	X(is_medusa),
 	X(atari_register_vme_int),
+	X(atari_unregister_vme_int),
 	X(stdma_lock),
 	X(stdma_release),
 	X(stdma_others_waiting),
diff -urN pc-2.0.21/arch/m68k/atari/stdma.c linux-2.0.21/arch/m68k/atari/stdma.c
--- pc-2.0.21/arch/m68k/atari/stdma.c	Tue Sep  3 19:10:17 1996
+++ linux-2.0.21/arch/m68k/atari/stdma.c	Sat Sep  7 11:53:18 1996
@@ -32,7 +32,7 @@
 #include <linux/types.h>
 #include <linux/genhd.h>
 #include <linux/sched.h>
-#include <asm/bootinfo.h>
+#include <asm/setup.h>
 #include <asm/atari_stdma.h>
 #include <asm/atariints.h>
 #include <asm/atarihw.h>
@@ -41,7 +41,8 @@
 #include <asm/irq.h>
 
 static int stdma_locked = 0;			/* the semaphore */
-static isrfunc	stdma_isr = NULL;		/* int func to be called */
+						/* int func to be called */
+static void (*stdma_isr)(int, void *, struct pt_regs *) = NULL;
 static void	*stdma_isr_data = NULL;		/* data passed to isr */
 static struct wait_queue *stdma_wait = NULL;	/* wait queue for ST-DMA */
 
@@ -50,7 +51,7 @@
 
 /***************************** Prototypes *****************************/
 
-static void stdma_int (int irq, struct pt_regs *fp, void *dummy);
+static void stdma_int (int irq, void *dummy, struct pt_regs *fp);
 
 /************************* End of Prototypes **************************/
 
@@ -72,7 +73,7 @@
  *
  */
 
-void stdma_lock(isrfunc isr, void *data)
+void stdma_lock(void (*handler)(int, void *, struct pt_regs *), void *data)
 {
 	unsigned long	oldflags;
 
@@ -86,7 +87,7 @@
 		sleep_on(&stdma_wait);
 
 	stdma_locked   = 1;
-	stdma_isr      = isr;
+	stdma_isr      = handler;
 	stdma_isr_data = data;
 	restore_flags(oldflags);
 }
@@ -174,8 +175,8 @@
 void stdma_init(void)
 {
 	stdma_isr = NULL;
-	add_isr(IRQ_MFP_FDC, stdma_int, IRQ_TYPE_SLOW, NULL,
-		"ST-DMA: floppy/ACSI/IDE/Falcon-SCSI");
+	request_irq(IRQ_MFP_FDC, stdma_int, IRQ_TYPE_SLOW,
+	            "ST-DMA: floppy/ACSI/IDE/Falcon-SCSI", stdma_int);
 }
 
 
@@ -187,8 +188,8 @@
  *
  */
 
-static void stdma_int(int irq, struct pt_regs *fp, void *dummy)
+static void stdma_int(int irq, void *dummy, struct pt_regs *fp)
 {
   if (stdma_isr)
-      (*stdma_isr)(irq, fp, stdma_isr_data);
+      (*stdma_isr)(irq, stdma_isr_data, fp);
 }
diff -urN pc-2.0.21/arch/m68k/atari/stram.c linux-2.0.21/arch/m68k/atari/stram.c
--- pc-2.0.21/arch/m68k/atari/stram.c	Wed Dec 27 21:44:28 1995
+++ linux-2.0.21/arch/m68k/atari/stram.c	Sat Aug 24 14:29:13 1996
@@ -2,7 +2,7 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
-#include <asm/bootinfo.h>
+#include <asm/setup.h>
 #include <asm/atarihw.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
diff -urN pc-2.0.21/arch/m68k/boot/Makefile linux-2.0.21/arch/m68k/boot/Makefile
--- pc-2.0.21/arch/m68k/boot/Makefile	Wed Dec 27 21:44:28 1995
+++ linux-2.0.21/arch/m68k/boot/Makefile	Wed Sep 11 20:28:44 1996
@@ -6,32 +6,57 @@
 # for more details.
 
 ifdef CONFIG_AMIGA
-AMIGA_BOOTOBJS := amiga/bootstrap.o
+AMIGA_BOOTSTRAP = amiga_bootstrap
+AMIGA_BOOTOBJS := amiga/bootstrap.o amiga/linuxboot.o
+AMIGA_HOSTCC = m68k-cbm-amigados-gcc -I$(TOPDIR)/include
+AMIGA_HOSTFLAGS=-m68030 -O2 -Wall -Dlinux
 endif
 
 ifdef CONFIG_ATARI
+ATARI_BOOTSTRAP = atari_bootstrap
 ATARI_BOOTOBJS := atari/bootstrap.o
-HOSTCC += -b m68k-mint
+ATARI_HOSTCC = m68k-mint-gcc -I$(TOPDIR)/include
+ATARI_HOSTFLAGS = -m68030 -m68881 -Dlinux -O2 -Wall
+
+# BOOTP/TFTP support in bootstrap?
+# USE_BOOTP = y
+
+ifdef USE_BOOTP
+ATARI_BOOTOBJS += atari/bootp.o
+ATARI_HOSTFLAGS += -DUSE_BOOTP
+
+# low-level Ethernet drivers:
+
+# Lance (RieblCard, PAM-VME)
+ATARI_BOOTOBJS += atari/ethlance.o
+ATARI_HOSTFLAGS += -DETHLL_LANCE
+
+endif
 endif
 
 ifdef CONFIG_ATARI
 atari_bootstrap: $(ATARI_BOOTOBJS)
-	$(HOSTCC) $(HOSTFLAGS) -o $@ $(ATARI_BOOTOBJS)
+	$(ATARI_HOSTCC) $(ATARI_HOSTFLAGS) -o $@ $(ATARI_BOOTOBJS)
 	rm -f ../../../bootstrap
 	ln $@ ../../../bootstrap
 endif
 
 ifdef CONFIG_AMIGA
 amiga_bootstrap: $(AMIGA_BOOTOBJS)
-	$(HOSTCC) $(HOSTFLAGS) -o $@ $(AMIGA_BOOTOBJS)
+	$(AMIGA_HOSTCC) $(AMIGA_HOSTFLAGS) -o $@ -s -noixemul $(AMIGA_BOOTOBJS)
 	rm -f ../../../bootstrap
 	ln $@ ../../../bootstrap
 endif
 
-$(AMIGA_BOOTOBJS) $(ATARI_BOOTOBJS): %.o: %.c
-	$(HOSTCC) $(HOSTFLAGS) -c $< -o $@
+$(AMIGA_BOOTOBJS): %.o: %.c
+	$(AMIGA_HOSTCC) $(AMIGA_HOSTFLAGS) -c $< -o $@
+
+$(ATARI_BOOTOBJS): %.o: %.c
+	$(ATARI_HOSTCC) $(ATARI_HOSTFLAGS) -c $< -o $@
+
+bootstrap: $(AMIGA_BOOTSTRAP) $(ATARI_BOOTSTRAP)
 
 clean:
-	rm -f *.o
+	rm -f *.o amiga/*.o atari/*.o amiga_bootstrap atari_bootstrap
 
 dep:
diff -urN pc-2.0.21/arch/m68k/boot/amiga/bootstrap.c linux-2.0.21/arch/m68k/boot/amiga/bootstrap.c
--- pc-2.0.21/arch/m68k/boot/amiga/bootstrap.c	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/boot/amiga/bootstrap.c	Wed Sep 11 20:28:51 1996
@@ -1,18 +1,26 @@
 /*
-** bootstrap.c -- This program loads the Linux/68k kernel into an Amiga
-**                and and launches it.
+** linux/arch/m68k/boot/amiga/bootstrap.c -- This program loads the Linux/m68k
+**					     kernel into an Amiga and launches
+**					     it.
 **
 ** Copyright 1993,1994 by Hamish Macdonald, Greg Harp
 **
 ** Modified 11-May-94 by Geert Uytterhoeven
-**                      (Geert.Uytterhoeven@cs.kuleuven.ac.be)
+**			(Geert.Uytterhoeven@cs.kuleuven.ac.be)
 **     - A3640 MapROM check
 ** Modified 31-May-94 by Geert Uytterhoeven
 **     - Memory thrash problem solved
 ** Modified 07-March-95 by Geert Uytterhoeven
 **     - Memory block sizes are rounded to a multiple of 256K instead of 1M
-**       This _requires_ >0.9pl5 to work!
-**       (unless all block sizes are multiples of 1M :-)
+**	 This _requires_ >0.9pl5 to work!
+**	 (unless all block sizes are multiples of 1M :-)
+** Modified 11-July-95 by Andreas Schwab
+**     - Support for ELF kernel (untested!)
+** Modified 10-Jan-96 by Geert Uytterhoeven
+**     - The real Linux/m68k boot code moved to linuxboot.[ch]
+** Modified 9-Sep-96 by Geert Uytterhoeven
+**     - Rewritten option parsing
+**     - New parameter passing to linuxboot() (linuxboot_args)
 **
 ** 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
@@ -23,760 +31,303 @@
 #include <stddef.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <stdarg.h>
 #include <string.h>
 #include <sys/file.h>
 #include <sys/types.h>
 #include <unistd.h>
 
-/* Amiga bootstrap include file */
-#include "bootstrap.h"
-
-/* required Linux/68k include files */
+/* required Linux/m68k include files */
 #include <linux/a.out.h>
-#include <asm/bootinfo.h>
-
-/* temporary stack size */
-#define TEMP_STACKSIZE	256
-
-/* Exec Base */
-extern struct ExecBase *SysBase;
-
-extern char *optarg;
-
-struct exec kexec;
-char *memptr;
-u_long start_mem;
-u_long mem_size;
-u_long rd_size;
-
-struct ExpansionBase *ExpansionBase;
-struct GfxBase *GfxBase;
-
-struct bootinfo bi;
-u_long bi_size = sizeof bi;
-
-caddr_t CustomBase = (caddr_t)CUSTOM_PHYSADDR;
-
-void usage(void)
-{
-	fprintf (stderr, "Usage:\n"
-		 "\tbootstrap [-d] [-k kernel_executable] [-r ramdisk_file]"
-		 " [option...]\n");
-	exit (EXIT_FAILURE);
-}
-
-/*
- * This assembler code is copied to chip ram, and
- * then executed.
- * It copies the kernel (and ramdisk) to their
- * final resting place.
- */
-#ifndef __GNUC__
-#error GNU CC is required to compile the bootstrap program
-#endif
-asm("
-.text
-.globl _copyall, _copyallend
-_copyall:
-				| /* put variables in registers because they may */
-	lea	_kexec,a3	| /* be overwritten by kernel/ramdisk copy!! - G.U. */
-	movel	_memptr,a4
-	movel	_start_mem,a5
-	movel	_mem_size,d0
-	movel	_rd_size,d1
-	movel	_bi_size,d5
-	movel	a3@(4),d2	| kexec.a_text
-	movel	a3@(8),d3	| kexec.a_data
-	movel	a3@(12),d4	| kexec.a_bss
-
-				| /* copy kernel text and data */
-	movel	a4,a0		| src = (u_long *)memptr;
-	movel	a0,a2		| limit = (u_long *)(memptr + kexec.a_text + kexec.a_data);
-	addl	d2,a2
-	addl	d3,a2
-	movel	a5,a1		| dest = (u_long *)start_mem;
-1:	cmpl	a0,a2
-	beqs	2f		| while (src < limit)
-	moveb	a0@+,a1@+	|	*dest++ = *src++;
-	bras	1b
-2:
-
-				| /* clear kernel bss */
-	movel	a1,a0		| dest = (u_long *)(start_mem + kexec.a_text + kexec.a_data);
-	movel	a1,a2		| limit = dest + kexec.a_bss / sizeof(u_long);
-	addl	d4,a2
-1:	cmpl	a0,a2
-	beqs	2f		| while (dest < limit)
-	clrb	a0@+		|	*dest++ = 0;
-	bras	1b
-2:
-
-				| /* copy bootinfo to end of bss */
-	movel	a4,a1		| src = (u long *)memptr + kexec.a_text + kexec.a_data);
-	addl	d2,a1
-	addl	d3,a1		| dest = end of bss (already in a0)
-	movel   d5,d7		| count = sizeof bi
-	subql	#1,d7
-1:	moveb	a1@+,a0@+	| while (--count > -1)
-	dbra	d7,1b		|	*dest++ = *src++
-	
-
-				| /* copy the ramdisk to the top of memory (from back to front) */
-	movel	a5,a1		| dest = (u_long *)(start_mem + mem_size);
-	addl	d0,a1
-	movel	a4,a2		| limit = (u_long *)(memptr + kexec.a_text + kexec.a_data + sizeof bi);
-	addl	d2,a2
-	addl	d3,a2
-        addl    d5,a2
-	movel	a2,a0		| src = (u_long *)((u_long)limit + rd_size);
-	addl	d1,a0
-1:	cmpl	a0,a2
-	beqs	2f		| while (src > limit)
-	moveb	a0@-,a1@-	| 	*--dest = *--src;
-	bras	1b
-2:
-				| /* jump to start of kernel */
-	movel	a5,a0		| jump_to (START_MEM);
-	jsr	a0@
-_copyallend:
-");
-
-asm("
-.text
-.globl _maprommed
-_maprommed:
-	oriw	#0x0700,sr
-	moveml	#0x3f20,sp@-
-/* Save cache settings */
-	.long 	0x4e7a1002	/* movec cacr,d1 */
-/* Save MMU settings */
-	.long 	0x4e7a2003	/* movec tc,d2 */
-	.long 	0x4e7a3004	/* movec itt0,d3 */
-	.long 	0x4e7a4005	/* movec itt1,d4 */
-	.long 	0x4e7a5006	/* movec dtt0,d5 */
-	.long 	0x4e7a6007	/* movec dtt1,d6 */
-	moveq	#0,d0
-	movel	d0,a2
-/* Disable caches */
-	.long 	0x4e7b0002	/* movec d0,cacr */
-/* Disable MMU */
-	.long 	0x4e7b0003	/* movec d0,tc */
-	.long 	0x4e7b0004	/* movec d0,itt0 */
-	.long 	0x4e7b0005	/* movec d0,itt1 */
-	.long 	0x4e7b0006	/* movec d0,dtt0 */
-	.long 	0x4e7b0007	/* movec d0,dtt1 */
-	lea	0x07f80000,a0
-	lea	0x00f80000,a1
-	movel	a0@,d7
-	cmpl	a1@,d7
-	jnes	1f
-	movel	d7,d0
-	notl	d0
-	movel	d0,a0@
-	nop
-	cmpl	a1@,d0
-	jnes	1f
-/* MapROMmed A3640 present */
-	moveq	#-1,d0
-	movel	d0,a2
-1:	movel	d7,a0@
-/* Restore MMU settings */
-	.long 	0x4e7b2003	/* movec d2,tc */
-	.long 	0x4e7b3004	/* movec d3,itt0 */
-	.long 	0x4e7b4005	/* movec d4,itt1 */
-	.long 	0x4e7b5006	/* movec d5,dtt0 */
-	.long 	0x4e7b6007	/* movec d6,dtt1 */
-/* Restore cache settings */
-	.long 	0x4e7b1002	/* movec d1,cacr */
-	movel	a2,d0
-	moveml	sp@+,#0x04fc
-	rte
-");
-
-extern unsigned long maprommed();
+#include <linux/elf.h>
+#include <asm/setup.h>
+#include <asm/page.h>
 
+/* Amiga bootstrap include files */
+#include "linuxboot.h"
+#include "bootstrap.h"
 
-extern char copyall, copyallend;
-
-int main(int argc, char *argv[])
-{
-	int ch, debugflag = 0, kfd, rfd = -1, i;
-	long fast_total = 0;	     /* total Fast RAM in system */
-	struct MemHeader *mnp;
-	struct ConfigDev *cdp = NULL;
-	char *kernel_name = "vmlinux";
-	char *ramdisk_name = NULL;
-	char *memfile = NULL;
-	u_long memreq;
-	void (*startfunc)(void);
-	long startcodesize;
-	u_long *stack, text_offset;
-	unsigned char *rb3_reg = NULL, *piccolo_reg = NULL, *sd64_reg = NULL;
-
-	/* print the greet message */
-	puts("Linux/68k Amiga Bootstrap version 1.11");
-	puts("Copyright 1993,1994 by Hamish Macdonald and Greg Harp\n");
-
-	/* machine is Amiga */
-	bi.machtype = MACH_AMIGA;
-
-	/* check arguments */
-	while ((ch = getopt(argc, argv, "dk:r:m:")) != EOF)
-		switch (ch) {
-		    case 'd':
-			debugflag = 1;
-			break;
-		    case 'k':
-			kernel_name = optarg;
-			break;
-		    case 'r':
-			ramdisk_name = optarg;
-			break;
-		    case 'm':
-			memfile = optarg;
-			break;
-		    case '?':
-		    default:
-			usage();
-		}
-	argc -= optind;
-	argv += optind;
-
-	SysBase = *(struct ExecBase **)4;
-
-	/* Memory & AutoConfig based on 'unix_boot.c' by C= */
-
-	/* open Expansion Library */
-	ExpansionBase = (struct ExpansionBase *)OpenLibrary("expansion.library", 36);
-	if (!ExpansionBase) {
-		puts("Unable to open expansion.library V36 or greater!  Aborting...");
-		exit(EXIT_FAILURE);
-	}
-
-	/* find all of the autoconfig boards in the system */
-	cdp = (struct ConfigDev *)FindConfigDev(cdp, -1, -1);
-	for (i=0; (i < NUM_AUTO) && cdp; i++) {
-		/* copy the contents of each structure into our boot info */
-		memcpy(&bi.bi_amiga.autocon[i], cdp, sizeof(struct ConfigDev));
 
-		/* count this device */
-		bi.bi_amiga.num_autocon++;
+/* Library Bases */
+extern const struct ExecBase *SysBase;
+const struct ExpansionBase *ExpansionBase;
+const struct GfxBase *GfxBase;
 
-		/* get next device */
-		cdp = (struct ConfigDev *)FindConfigDev(cdp, -1, -1);
-	}
+static const char *memfile_name = NULL;
 
-	/* find out the memory in the system */
-	for (mnp = (struct MemHeader *)SysBase->MemList.l_head;
-	     (bi.num_memory < NUM_MEMINFO) && mnp->mh_Node.ln_Succ;
-	     mnp = (struct MemHeader *)mnp->mh_Node.ln_Succ)
-	{
-		struct MemHeader mh;
-
-		/* copy the information */
-		mh = *mnp;
-
-		/* if we suspect that Kickstart is shadowed in an A3000,
-		   modify the entry to show 512K more at the top of RAM
-		   Check first for a MapROMmed A3640 board: overwriting the
-		   Kickstart image causes an infinite lock-up on reboot! */
-
-		if (mh.mh_Upper == (void *)0x07f80000)
-			if ((SysBase->AttnFlags & AFF_68040) && Supervisor(maprommed))
-				printf("A3640 MapROM detected.\n");
-			else {
-				mh.mh_Upper = (void *)0x08000000;
-				printf("A3000 shadowed Kickstart detected.\n");
-			}
-
-		/* if we suspect that Kickstart is zkicked,
-		   modify the entry to show 512K more at the bottom of RAM */
-		if (mh.mh_Lower == (void *)0x00280020) {
-		    mh.mh_Lower =  (void *)0x00200000;
-		    printf("ZKick detected.\n");
-		}
-
-		/*
-		 * If this machine has "LOCAL" memory between 0x07000000
-		 * and 0x080000000, then we'll call it an A3000.
-		 */
-		if (mh.mh_Lower >= (void *)0x07000000 &&
-		    mh.mh_Lower <  (void *)0x08000000 &&
-		    (mh.mh_Attributes & MEMF_LOCAL))
-			bi.bi_amiga.model = AMI_3000;
-
-		/* mask the memory limit values */
-		mh.mh_Upper = (void *)((u_long)mh.mh_Upper & 0xfffff000);
-		mh.mh_Lower = (void *)((u_long)mh.mh_Lower & 0xfffff000);
-
-		/* if fast memory */
-		if (mh.mh_Attributes & MEMF_FAST) {
-			unsigned long size;
-
-			/* record the start */
-			bi.memory[bi.num_memory].addr = (u_long)mh.mh_Lower;
-
-			/* set the size value to the size of this block */
-			size = (u_long)mh.mh_Upper - (u_long)mh.mh_Lower;
-
-			/* mask off to a 256K increment */
-			size &= 0xfffc0000;
-
-			fast_total += size;
-
-			if (size > 0)
-				/* count this block */
-				bi.memory[bi.num_memory++].size  = size;
-
-		} else if (mh.mh_Attributes & MEMF_CHIP) {
-			/* if CHIP memory, record the size */
-			bi.bi_amiga.chip_size =
-				(u_long)mh.mh_Upper; /* - (u_long)mh.mh_Lower; */
-		}
-	}
+static int model = AMI_UNKNOWN;
 
-	CloseLibrary((struct Library *)ExpansionBase);
+static const char *ProgramName;
 
-	/*
-	 * if we have a memory file, read the memory information from it
-	 */
-	if (memfile) {
-	    FILE *fp;
-	    int i;
-
-	    if ((fp = fopen (memfile, "r")) == NULL) {
-		perror ("open memory file");
-		fprintf (stderr, "Cannot open memory file %s\n", memfile);
-		exit (EXIT_FAILURE);
-	    }
+struct linuxboot_args args;
 
-	    if (fscanf (fp, "%lu", &bi.bi_amiga.chip_size) != 1) {
-		fprintf (stderr, "memory file does not contain chip memory size\n");
-		fclose (fp);
-		exit (EXIT_FAILURE);
-	    }
-		
-	    for (i = 0; i < NUM_MEMINFO; i++) {
-		if (fscanf (fp, "%lx %lu", &bi.memory[i].addr,
-			    &bi.memory[i].size) != 2)
-		    break;
-	    }
 
-	    fclose (fp);
+    /*
+     *  Function Prototypes
+     */
 
-	    if (i != bi.num_memory && i > 0)
-		bi.num_memory = i;
-	}
+static void Usage(void) __attribute__ ((noreturn));
+int main(int argc, char *argv[]);
+static void Puts(const char *str);
+static long GetChar(void);
+static void PutChar(char c);
+static void Printf(const char *fmt, ...);
+static int Open(const char *path);
+static int Seek(int fd, int offset);
+static int Read(int fd, char *buf, int count);
+static void Close(int fd);
+static int FileSize(const char *path);
+static void Sleep(u_long micros);
+static int ModifyBootinfo(struct bootinfo *bi);
 
-	/* get info from ExecBase */
-	bi.bi_amiga.vblank = SysBase->VBlankFrequency;
-	bi.bi_amiga.psfreq = SysBase->PowerSupplyFrequency;
-	bi.bi_amiga.eclock = SysBase->EClockFrequency;
-
-	/* open graphics library */
-	GfxBase = (struct GfxBase *)OpenLibrary ("graphics.library", 0);
-
-	/* determine chipset */
-	bi.bi_amiga.chipset = CS_STONEAGE;
-	if(GfxBase)
-	{
-	    if(GfxBase->ChipRevBits0 & GFXG_AGA)
-	    {
-		bi.bi_amiga.chipset = CS_AGA;
-		/*
-		 *  we considered this machine to be an A3000 because of its
-		 *  local memory just beneath $8000000; now if it has AGA, it
-		 *  must be an A4000
-		 *  except the case no RAM is installed on the motherboard but
-		 *  on an additional card like FastLane Z3 or on the processor
-		 *  board itself. Gotta check this out.
-		 */
-		bi.bi_amiga.model =
-		    (bi.bi_amiga.model == AMI_3000) ? AMI_4000 : AMI_1200;
-	    }
-	    else if(GfxBase->ChipRevBits0 & GFXG_ECS)
-		bi.bi_amiga.chipset = CS_ECS;
-	    else if(GfxBase->ChipRevBits0 & GFXG_OCS)
-		bi.bi_amiga.chipset = CS_OCS;
-	}
 
-	/* Display amiga model */
-	switch (bi.bi_amiga.model) {
-	    case AMI_UNKNOWN:
-		break;
-	    case AMI_500:
-		printf ("Amiga 500 ");
-		break;
-	    case AMI_2000:
-		printf ("Amiga 2000 ");
-		break;
-	    case AMI_3000:
-		printf ("Amiga 3000 ");
-		break;
-	    case AMI_4000:
-		printf ("Amiga 4000 ");
-		break;
-	    case AMI_1200:		/* this implies an upgraded model   */
-		printf ("Amiga 1200 "); /* equipped with at least 68030 !!! */
-		break;
-	}
-
-	/* display and set the CPU <type */
-	printf("CPU: ");
-	if (SysBase->AttnFlags & AFF_68040) {
-		printf("68040");
-		bi.cputype = CPU_68040;
-		if (SysBase->AttnFlags & AFF_FPU40) {
-			printf(" with internal FPU");
-			bi.cputype |= FPU_68040;
-		} else
-			printf(" without FPU");
-	} else {
-		if (SysBase->AttnFlags & AFF_68030) {
-			printf("68030");
-			bi.cputype = CPU_68030;
-		} else if (SysBase->AttnFlags & AFF_68020) {
-			printf("68020 (Do you have an MMU?)");
-			bi.cputype = CPU_68020;
-		} else {
-			puts("Insufficient for Linux.  Aborting...");
-			printf("SysBase->AttnFlags = %#x\n", SysBase->AttnFlags);
-			exit (EXIT_FAILURE);
-		}
-		if (SysBase->AttnFlags & AFF_68882) {
-			printf(" with 68882 FPU");
-			bi.cputype |= FPU_68882;
-		} else if (SysBase->AttnFlags & AFF_68881) {
-			printf(" with 68881 FPU");
-			bi.cputype |= FPU_68881;
-		} else
-			printf(" without FPU");
-	}
-
-	switch(bi.bi_amiga.chipset)
-	{
-	    case CS_STONEAGE:
-		printf(", old or unknown chipset");
-		break;
-	    case CS_OCS:
-		printf(", OCS");
-		break;
-	    case CS_ECS:
-		printf(", ECS");
-		break;
-	    case CS_AGA:
-		printf(", AGA chipset");
-		break;
-	}
-
-	putchar ('\n');
-	putchar ('\n');
-
-	/*
-	 * Copy command line options into the kernel command line.
-	 */
-	i = 0;
-	while (argc--) {
-		if ((i+strlen(*argv)+1) < CL_SIZE) {
-			i += strlen(*argv) + 1;
-			if (bi.command_line[0])
-				strcat (bi.command_line, " ");
-			strcat (bi.command_line, *argv++);
-		}
-	}
-	printf ("Command line is '%s'\n", bi.command_line);
-
-	/* display the clock statistics */
-	printf("Vertical Blank Frequency: %dHz\nPower Supply Frequency: %dHz\n",
-	       bi.bi_amiga.vblank, bi.bi_amiga.psfreq);
-	printf("EClock Frequency: %7.5fKHz\n\n",
-	       (float)bi.bi_amiga.eclock / 1000);
-
-	/* display autoconfig devices */
-	if (bi.bi_amiga.num_autocon) {
-		printf("Found %d AutoConfig Device%s", bi.bi_amiga.num_autocon,
-		       (bi.bi_amiga.num_autocon > 1)?"s\n":"\n");
-		for (i=0; i<bi.bi_amiga.num_autocon; i++)
-		{
-			printf("Device %d: addr = %08lx\n", i,
-			       (u_long)bi.bi_amiga.autocon[i].cd_BoardAddr);
-			/* check for a Rainbow 3 and prepare to reset it if there is one */
-			if ( (bi.bi_amiga.autocon[i].cd_Rom.er_Manufacturer == MANUF_HELFRICH1) &&
-				 (bi.bi_amiga.autocon[i].cd_Rom.er_Product == PROD_RAINBOW3) )
-			{
-				printf("(Found a Rainbow 3 board - will reset it at kernel boot time)\n");
-				rb3_reg = (unsigned char *)(bi.bi_amiga.autocon[i].cd_BoardAddr + 0x01002000);
-			}
-
-			/* check for a Piccolo and prepare to reset it if there is one */
-			if ( (bi.bi_amiga.autocon[i].cd_Rom.er_Manufacturer == MANUF_HELFRICH2) &&
-				 (bi.bi_amiga.autocon[i].cd_Rom.er_Product == PROD_PICCOLO_REG) )
-			{
-				printf("(Found a Piccolo board - will reset it at kernel boot time)\n");
-				piccolo_reg = (unsigned char *)(bi.bi_amiga.autocon[i].cd_BoardAddr + 0x8000);
-			}
-
-			/* check for a SD64 and prepare to reset it if there is one */
-			if ( (bi.bi_amiga.autocon[i].cd_Rom.er_Manufacturer == MANUF_HELFRICH2) &&
-				 (bi.bi_amiga.autocon[i].cd_Rom.er_Product == PROD_SD64_REG) )
-			{
-				printf("(Found a SD64 board - will reset it at kernel boot time)\n");
-				sd64_reg = (unsigned char *)(bi.bi_amiga.autocon[i].cd_BoardAddr + 0x8000);
-			}
-
-			/* what this code lacks - what if there are several boards of  */
-			/* the same brand ? In that case I should reset them one after */
-			/* the other, which is currently not done - a rare case...FN   */
-			/* ok, MY amiga currently hosts all three of the above boards ;-) */
-		}
-	} else
-		puts("No AutoConfig Devices Found");
-
-	/* display memory */
-	if (bi.num_memory) {
-		printf("\n%d Block%sof Memory Found\n", bi.num_memory,
-		       (bi.num_memory > 1)?"s ":" ");
-		for (i=0; i<bi.num_memory; i++) {
-			printf("Block %d: %08lx to %08lx (%ldKB)\n",
-			       i, bi.memory[i].addr,
-			       bi.memory[i].addr + bi.memory[i].size,
-			       bi.memory[i].size >> 10);
-		}
-	} else {
-		puts("No memory found?!  Aborting...");
-		exit(10);
-	}
+static void Usage(void)
+{
+    fprintf(stderr,
+	    "Linux/m68k Amiga Bootstrap version " AMIBOOT_VERSION "\n\n"
+	    "Usage: %s [options] [kernel command line]\n\n"
+	    "Valid options are:\n"
+	    "    -h, --help           Display this usage information\n"
+	    "    -k, --kernel file    Use kernel image `file' (default is `vmlinux')\n"
+	    "    -r, --ramdisk file   Use ramdisk image `file'\n"
+	    "    -d, --debug          Enable debug mode\n"
+	    "    -m, --memfile file   Use memory file `file'\n"
+	    "    -v, --keep-video     Don't reset the video mode\n"
+	    "    -t, --model id       Set the Amiga model to `id'\n\n",
+	    ProgramName);
+    exit(EXIT_FAILURE);
+}
 
-	/* display chip memory size */
-	printf ("%ldK of CHIP memory\n", bi.bi_amiga.chip_size >> 10);
 
-	start_mem = bi.memory[0].addr;
-	mem_size = bi.memory[0].size;
+int main(int argc, char *argv[])
+{
+    int i;
+    int debugflag = 0, keep_video = 0;
+    const char *kernel_name = NULL;
+    const char *ramdisk_name = NULL;
+    char commandline[CL_SIZE] = "";
+
+    ProgramName = argv[0];
+    while (--argc) {
+	argv++;
+	if (!strcmp(argv[0], "-h") || !strcmp(argv[0], "--help"))
+	    Usage();
+	else if (!strcmp(argv[0], "-k") || !strcmp(argv[0], "--kernel"))
+            if (--argc && !kernel_name) {
+                kernel_name = argv[1];
+                argv++;
+            } else
+                Usage();
+	else if (!strcmp(argv[0], "-r") || !strcmp(argv[0], "--ramdisk"))
+            if (--argc && !ramdisk_name) {
+                ramdisk_name = argv[1];
+                argv++;
+            } else
+                Usage();
+	else if (!strcmp(argv[0], "-d") || !strcmp(argv[0], "--debug"))
+	    debugflag = 1;
+	else if (!strcmp(argv[0], "-m") || !strcmp(argv[0], "--memfile"))
+            if (--argc && !memfile_name) {
+                memfile_name = argv[1];
+                argv++;
+            } else
+                Usage();
+	else if (!strcmp(argv[0], "-v") || !strcmp(argv[0], "--keep-video"))
+	    keep_video = 1;
+	else if (!strcmp(argv[0], "-t") || !strcmp(argv[0], "--model"))
+            if (--argc && !model) {
+                model = atoi(argv[1]);
+                argv++;
+            } else
+                Usage();
+	else
+	    break;
+    }
+    if (!kernel_name)
+	kernel_name = "vmlinux";
+
+    SysBase = *(struct ExecBase **)4;
+
+    /* open Expansion Library */
+    ExpansionBase = (struct ExpansionBase *)OpenLibrary("expansion.library",
+							36);
+    if (!ExpansionBase) {
+	fputs("Unable to open expansion.library V36 or greater!  Aborting...\n",
+	      stderr);
+	exit(EXIT_FAILURE);
+   }
+
+    /* open Graphics Library */
+    GfxBase = (struct GfxBase *)OpenLibrary ("graphics.library", 0);
+    if (!GfxBase) {
+	fputs("Unable to open graphics.library!  Aborting...\n", stderr);
+	exit(EXIT_FAILURE);
+    }
+
+    /*
+     *	Join command line options
+     */
+    i = 0;
+    while (argc--) {
+	if ((i+strlen(*argv)+1) < CL_SIZE) {
+	    i += strlen(*argv) + 1;
+	    if (commandline[0])
+		strcat(commandline, " ");
+	    strcat(commandline, *argv++);
+	}
+    }
+
+    args.kernelname = kernel_name;
+    args.ramdiskname = ramdisk_name;
+    args.commandline = commandline;
+    args.debugflag = debugflag;
+    args.keep_video = keep_video;
+    args.reset_boards = 1;
+    args.puts = Puts;
+    args.getchar = GetChar;
+    args.putchar = PutChar;
+    args.printf = Printf;
+    args.open = Open;
+    args.seek = Seek;
+    args.read = Read;
+    args.close = Close;
+    args.filesize = FileSize;
+    args.sleep = Sleep;
+    args.modify_bootinfo = ModifyBootinfo;
 
-	/* tell us where the kernel will go */
-	printf("\nThe kernel will be located at %08lx\n", start_mem);
+    /* Do The Right Stuff */
+    linuxboot(&args);
 
-	/* verify that there is enough Chip RAM */
-	if (bi.bi_amiga.chip_size < 512*1024) {
-		puts("\nNot enough Chip RAM in this system.  Aborting...");
-		exit(10);
-	}
+    CloseLibrary((struct Library *)GfxBase);
+    CloseLibrary((struct Library *)ExpansionBase);
 
-	/* verify that there is enough Fast RAM */
-	if (fast_total < 2*1024*1024) {
-		puts("\nNot enough Fast RAM in this system.  Aborting...");
-		exit(10);
-	}
+    /* if we ever get here, something went wrong */
+    exit(EXIT_FAILURE);
+}
 
-	/* open kernel executable and read exec header */
-	if ((kfd = open (kernel_name, O_RDONLY)) == -1) {
-		fprintf (stderr, "Unable to open kernel file %s\n", kernel_name);
-		exit (EXIT_FAILURE);
-	}
 
-	if (read (kfd, (void *)&kexec, sizeof(kexec)) != sizeof(kexec)) {
-		fprintf (stderr, "Unable to read exec header from %s\n",
-			 kernel_name);
-		exit (EXIT_FAILURE);
-	}
+    /*
+     *  Routines needed by linuxboot
+     */
 
-	switch (N_MAGIC(kexec)) {
-	  case ZMAGIC:
-	    text_offset = N_TXTOFF(kexec);
-	    break;
-	  case QMAGIC:
-	    text_offset = sizeof(kexec);
-	    /* the text size includes the exec header; remove this */
-	    kexec.a_text -= sizeof(kexec);
-	    break;
-	  default:
-	    fprintf (stderr, "Wrong magic number %lo in kernel header\n",
-		     N_MAGIC(kexec));
-	    exit (EXIT_FAILURE);
-	}
+static void Puts(const char *str)
+{
+    fputs(str, stderr);
+}
 
-	/* Load the kernel at one page after start of mem */
-	start_mem += PAGE_SIZE;
-	mem_size -= PAGE_SIZE;
-	/* Align bss size to multiple of four */
-	kexec.a_bss = (kexec.a_bss + 3) & ~3;
-
-	if (ramdisk_name) {
-		if ((rfd = open (ramdisk_name, O_RDONLY)) == -1) {
-			fprintf (stderr, "Unable to open ramdisk file %s\n",
-				 ramdisk_name);
-			exit (EXIT_FAILURE);
-		}
-		/* record ramdisk size */
-		bi.ramdisk_size = (lseek (rfd, 0, L_XTND) + 1023) >> 10;
-	} else
-		bi.ramdisk_size = 0;
-
-	rd_size = bi.ramdisk_size << 10;
-	bi.ramdisk_addr = (u_long)start_mem + mem_size - rd_size;
-
-	memreq = kexec.a_text + kexec.a_data + sizeof(bi) + rd_size;
-	if (!(memptr = (char *)AllocMem (memreq, MEMF_FAST | MEMF_CLEAR))) {
-		fprintf (stderr, "Unable to allocate memory\n");
-		exit (EXIT_FAILURE);
-	}
+static long GetChar(void)
+{
+    return(getchar());
+}
 
-	if (lseek (kfd, text_offset, L_SET) == -1) {
-		fprintf (stderr, "Failed to seek to text\n");
-		FreeMem ((void *)memptr, memreq);
-		exit (EXIT_FAILURE);
-	}
-	if (read (kfd, memptr, kexec.a_text) != kexec.a_text) {
-		fprintf (stderr, "Failed to read text\n");
-		FreeMem ((void *)memptr, memreq);
-		exit (EXIT_FAILURE);
-	}
+static void PutChar(char c)
+{
+    fputc(c, stderr);
+}
 
-	/* data follows immediately after text */
-	if (read (kfd, memptr + kexec.a_text, kexec.a_data) != kexec.a_data) {
-		fprintf (stderr, "Failed to read data\n");
-		FreeMem ((void *)memptr, memreq);
-		exit (EXIT_FAILURE);
-	}
-	close (kfd);
+static void Printf(const char *fmt, ...)
+{
+    va_list args;
 
-	/* copy the boot_info struct to the end of the kernel image */
-	memcpy ((void *)(memptr + kexec.a_text + kexec.a_data), &bi,
-		sizeof(bi));
-
-	if (rfd != -1) {
-		if (lseek (rfd, 0, L_SET) == -1) {
-			fprintf (stderr, "Failed to seek to beginning of ramdisk file\n");
-			FreeMem ((void *)memptr, memreq);
-			exit (EXIT_FAILURE);
-		}
-		if (read (rfd, memptr + kexec.a_text + kexec.a_data 
-			  + sizeof(bi), rd_size) != rd_size) {
-			fprintf (stderr, "Failed to read ramdisk file\n");
-			FreeMem ((void *)memptr, memreq);
-			exit (EXIT_FAILURE);
-		}
-		close (rfd);
-	}
+    va_start(args, fmt);
+    vfprintf(stderr, fmt, args);
+    va_end(args);
+}
 
-	/* allocate temporary chip ram stack */
-	stack = (u_long *)AllocMem( TEMP_STACKSIZE, MEMF_CHIP|MEMF_CLEAR);
-	if (!stack) {
-		fprintf (stderr, "Unable to allocate memory for stack\n");
-		FreeMem ((void *)memptr, memreq);
-		exit (EXIT_FAILURE);
-	}
+static int Open(const char *path)
+{
+    return(open(path, O_RDONLY));
+}
 
-	/* allocate chip ram for copy of startup code */
-	startcodesize = &copyallend - &copyall;
-	startfunc = (void (*)(void))AllocMem( startcodesize, MEMF_CHIP);
-	if (!startfunc) {
-		fprintf (stderr, "Unable to allocate memory for code\n");
-		FreeMem ((void *)memptr, memreq);
-		FreeMem ((void *)stack, TEMP_STACKSIZE);
-		exit (EXIT_FAILURE);
-	}
+static int Seek(int fd, int offset)
+{
+    return(lseek(fd, offset, SEEK_SET));
+}
 
-	/* copy startup code to CHIP RAM */
-	memcpy (startfunc, &copyall, startcodesize);
 
-	if (debugflag) {
-		if (bi.ramdisk_size)
-			printf ("RAM disk at %#lx, size is %ldK\n",
-				(u_long)memptr + kexec.a_text + kexec.a_data,
-				bi.ramdisk_size);
-
-		printf ("\nKernel text at %#lx, code size %x\n",
-			start_mem, kexec.a_text);
-		printf ("Kernel data at %#lx, data size %x\n",
-			start_mem + kexec.a_text, kexec.a_data );
-		printf ("Kernel bss  at %#lx, bss  size %x\n",
-			start_mem + kexec.a_text + kexec.a_data,
-			kexec.a_bss );
-		printf ("boot info at %#lx\n", start_mem + kexec.a_text
-			+ kexec.a_data + kexec.a_bss);
-
-		printf ("\nKernel entry is %#x\n", kexec.a_entry );
-
-		printf ("ramdisk dest top is %#lx\n", start_mem + mem_size);
-		printf ("ramdisk lower limit is %#lx\n",
-			(u_long)(memptr + kexec.a_text + kexec.a_data));
-		printf ("ramdisk src top is %#lx\n",
-			(u_long)(memptr + kexec.a_text + kexec.a_data)
-			+ rd_size);
-
-		printf ("Type a key to continue the Linux boot...");
-		fflush (stdout);
-		getchar();
-	}
+static int Read(int fd, char *buf, int count)
+{
+    return(read(fd, buf, count));
+}
 
-	/* wait for things to settle down */
-	sleep(2);
+static void Close(int fd)
+{
+    close(fd);
+}
 
-	/* FN: If a Rainbow III board is present, reset it to disable */
-	/* its (possibly activated) vertical blank interrupts as the */
-	/* kernel is not yet prepared to handle them (level 6). */
-	if (rb3_reg != NULL)
-	{
-		/* set RESET bit in special function register */
-		*rb3_reg = 0x01;
-		/* actually, only a few cycles delay are required... */
-		sleep(1);
-		/* clear reset bit */
-		*rb3_reg = 0x00;
-	}
+static int FileSize(const char *path)
+{
+    int fd, size = -1;
 
-	/* the same stuff as above, for the Piccolo board. */
-	/* this also has the side effect of resetting the board's */
-	/* output selection logic to use the Amiga's display in single */
-	/* monitor systems - which is currently what we want. */
-	if (piccolo_reg != NULL)
-	{
-		/* set RESET bit in special function register */
-		*piccolo_reg = 0x01;
-		/* actually, only a few cycles delay are required... */
-		sleep(1);
-		/* clear reset bit */
-		*piccolo_reg = 0x51;
-	}
+    if ((fd = open(path, O_RDONLY)) != -1) {
+        size = lseek(fd, 0, SEEK_END);
+        close(fd);
+    }
+    return(size);
+}
 
-	/* the same stuff as above, for the SD64 board. */
-	/* just as on the Piccolo, this also resets the monitor switch */
-	if (sd64_reg != NULL)
-	{
-		/* set RESET bit in special function register */
-		*sd64_reg = 0x1f;
-		/* actually, only a few cycles delay are required... */
-		sleep(1);
-	/* clear reset bit AND switch monitor bit (0x20) */
-	*sd64_reg = 0x4f;
-	}
+static void Sleep(u_long micros)
+{
+    struct MsgPort *TimerPort;
+    struct timerequest *TimerRequest;
 
-	if (GfxBase) {
-		/* set graphics mode to a nice normal one */
-		LoadView (NULL);
-		CloseLibrary ((struct Library *)GfxBase);
+    if ((TimerPort = CreateMsgPort())) {
+	if ((TimerRequest = CreateIORequest(TimerPort,
+					    sizeof(struct timerequest)))) {
+	    if (!OpenDevice("timer.device", UNIT_VBLANK,
+			    (struct IORequest *)TimerRequest, 0)) {
+		TimerRequest->io_Command = TR_ADDREQUEST;
+		TimerRequest->io_Flags = IOF_QUICK;
+		TimerRequest->tv_secs = micros/1000000;
+		TimerRequest->tv_micro = micros%1000000;
+		DoIO((struct IORequest *)TimerRequest);
+		CloseDevice((struct IORequest *)TimerRequest);
+	    }
+	    DeleteIORequest(TimerRequest);
 	}
+	DeleteMsgPort(TimerPort);
+    }
+}
 
-	Disable();
-
-	/* Turn off all DMA */
-	custom.dmacon = DMAF_ALL | DMAF_MASTER;
-
-	/* turn off caches */
-	CacheControl (0L, ~0L);
-
-	/* Go into supervisor state */
-	SuperState ();
-
-	/* setup stack */
-	change_stack ((char *) stack + TEMP_STACKSIZE);
-
-	/* turn off any mmu translation */
-	disable_mmu ();
 
-	/* execute the copy-and-go code (from CHIP RAM) */
-	startfunc();
+static int ModifyBootinfo(struct bootinfo *bi)
+{
+   /*
+    * if we have a memory file, read the memory information from it
+    */
+   if (memfile_name) {
+      FILE *fp;
+      int i;
+
+      if ((fp = fopen(memfile_name, "r")) == NULL) {
+         perror("open memory file");
+         fprintf(stderr, "Cannot open memory file %s\n", memfile_name);
+         return(FALSE);
+      }
+
+      if (fscanf(fp, "%lu", &bi->bi_amiga.chip_size) != 1) {
+         fprintf(stderr, "memory file does not contain chip memory size\n");
+         fclose(fp);
+         return(FALSE);
+      }
+                
+      for (i = 0; i < NUM_MEMINFO; i++) {
+         if (fscanf(fp, "%lx %lu", &bi->memory[i].addr, &bi->memory[i].size)
+	     != 2)
+            break;
+      }
+
+      fclose(fp);
+
+      if (i != bi->num_memory && i > 0)
+         bi->num_memory = i;
+   }
+
+   /*
+    * change the Amiga model, if necessary
+    */
+   if (model != AMI_UNKNOWN)
+      bi->bi_amiga.model = model;
 
-	/* NOTREACHED */
+   return(TRUE);
 }
diff -urN pc-2.0.21/arch/m68k/boot/amiga/bootstrap.h linux-2.0.21/arch/m68k/boot/amiga/bootstrap.h
--- pc-2.0.21/arch/m68k/boot/amiga/bootstrap.h	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/boot/amiga/bootstrap.h	Wed Sep 11 20:28:52 1996
@@ -1,12 +1,18 @@
 /*
-** bootstrap.h -- This file is a part of the Amiga bootloader.
+** linux/arch/m68k/boot/amiga/bootstrap.h -- This file is part of the Amiga
+**					     bootloader.
 **
 ** Copyright 1993, 1994 by Hamish Macdonald
 **
 ** Some minor additions by Michael Rausch 1-11-94
 ** Modified 11-May-94 by Geert Uytterhoeven
-**                      (Geert.Uytterhoeven@cs.kuleuven.ac.be)
+**			(Geert.Uytterhoeven@cs.kuleuven.ac.be)
 **     - inline Supervisor() call
+** Modified 10-Jan-96 by Geert Uytterhoeven
+**     - The real Linux/m68k boot code moved to linuxboot.[ch]
+** Modified 9-Sep-96 by Geert Uytterhoeven
+**     - const library bases
+**     - fixed register naming for m68k-cbm-amigados-gcc
 **
 ** 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
@@ -14,316 +20,156 @@
 **
 */
 
-#ifndef BOOTSTRAP_H
-#define BOOTSTRAP_H
-
 #include <asm/amigatypes.h>
-#include <asm/amigahw.h>
 
-struct List {
-    struct Node *l_head;
-    struct Node *l_tail;
-    struct Node *l_tailpred;
-    u_char  l_type;
-    u_char  l_pad;
-};
 
-struct MemChunk {
-    struct  MemChunk *mc_Next;	/* pointer to next chunk */
-    u_long   mc_Bytes;		/* chunk byte size	*/
+struct MsgPort {
+    u_char fill1[15];
+    u_char mp_SigBit;
+    u_char fill2[18];
 };
 
-#define MEMF_CHIP  (1<<1)
-#define MEMF_FAST  (1<<2)
-#define MEMF_LOCAL (1<<8)
-#define MEMF_CLEAR (1<<16)
-
-struct MemHeader {
-    struct  Node mh_Node;
-    u_short   mh_Attributes;	/* characteristics of this region */
-    struct  MemChunk *mh_First; /* first free region		*/
-    void    *mh_Lower;		/* lower memory bound		*/
-    void    *mh_Upper;		/* upper memory bound+1 */
-    u_long   mh_Free;		/* total number of free bytes	*/
+struct IOStdReq {
+    u_char fill1[20];
+    struct Device *io_Device;
+    u_char fill2[4];
+    u_short io_Command;
+    u_char io_Flags;
+    char io_Error;
+    u_long io_Actual;
+    u_long io_Length;
+    void *io_Data;
+    u_char fill4[4];
 };
 
-struct ExecBase {
-    u_char	fill1[296];
-    u_short	AttnFlags;
-    u_char	fill2[24];
-    struct List MemList;
-    u_char	fill3[194];
-    u_char	VBlankFrequency;
-    u_char	PowerSupplyFrequency;
-    u_char	fill4[36];
-    u_long	EClockFrequency;
-};
+#define IOF_QUICK	(1<<0)
 
-#ifndef AFF_68020
-#define AFB_68020 1
-#define AFF_68020 (1<<AFB_68020)
-#endif
-
-#ifndef AFF_68030
-#define AFB_68030 2
-#define AFF_68030 (1<<AFB_68030)
-#endif
-
-#ifndef AFF_68040
-#define AFB_68040 3
-#define AFF_68040 (1<<AFB_68040)
-#endif
-
-#ifndef AFF_68881
-#define AFB_68881 4
-#define AFF_68881 (1<<AFB_68881)
-#endif
-
-#ifndef AFF_68882
-#define AFB_68882 5
-#define AFF_68882 (1<<AFB_68882)
-#endif
-
-#ifndef AFF_FPU40
-#define AFB_FPU40 6
-#define AFF_FPU40 (1<<AFB_FPU40)
-#endif
-
-/*
- *  GfxBase is now used to determine if AGA or ECS is present
- */
-
-struct GfxBase {
-	u_char	unused1[0xec];
-	u_char	ChipRevBits0;
-	u_char	unused2[5];
-	u_short	monitor_id;
+struct timerequest {
+    u_char fill1[28];
+    u_short io_Command;
+    u_char io_Flags;
+    u_char fill2[1];
+    u_long tv_secs;
+    u_long tv_micro;
 };
 
-#ifndef	GFXB_HR_AGNUS
-#define	GFXB_HR_AGNUS	0
-#define	GFXF_HR_AGNUS	(1<<GFXB_HR_AGNUS)
-#endif
-
-#ifndef	GFXB_HR_DENISE
-#define GFXB_HR_DENISE	1
-#define GFXF_HR_DENISE	(1<<GFXB_HR_DENISE)
-#endif
-
-#ifndef	GFXB_AA_ALICE
-#define GFXB_AA_ALICE	2
-#define GFXF_AA_ALICE	(1<<GFXB_AA_ALICE)
-#endif
-
-#ifndef	GFXB_AA_LISA
-#define GFXB_AA_LISA	3
-#define GFXF_AA_LISA	(1<<GFXB_AA_LISA)
-#endif
-
-/*
- *  HiRes(=Big) Agnus present; i.e. 
- *  1MB chipmem, big blits (none of interest so far) and programmable sync
- */
-#define GFXG_OCS	(GFXF_HR_AGNUS)
-/*
- *  HiRes Agnus/Denise present; we are running on ECS
- */
-#define GFXG_ECS	(GFXF_HR_AGNUS|GFXF_HR_DENISE)
-/*
- *  Alice and Lisa present; we are running on AGA
- */
-#define GFXG_AGA	(GFXF_AA_ALICE|GFXF_AA_LISA)
+#define UNIT_VBLANK	1
+#define TR_ADDREQUEST	9
+
 
 struct Library;
+struct IORequest;
 
-extern struct ExecBase *SysBase;
 
-static __inline void *
-AllocMem (unsigned long byteSize,unsigned long requirements)
-{
-  register void  *_res	__asm("d0");
-  register struct ExecBase *a6 __asm("a6") = SysBase;
-  register unsigned long d0 __asm("d0") = byteSize;
-  register unsigned long d1 __asm("d1") = requirements;
-  __asm __volatile ("jsr a6@(-0xc6)"
-  : "=r" (_res)
-  : "r" (a6), "r" (d0), "r" (d1)
-  : "a0","a1","d0","d1", "memory");
-  return _res;
-}
-static __inline void
-CloseLibrary (struct Library *library)
-{
-  register struct ExecBase *a6 __asm("a6") = SysBase;
-  register struct Library *a1 __asm("a1") = library;
-  __asm __volatile ("jsr a6@(-0x19e)"
-  : /* no output */
-  : "r" (a6), "r" (a1)
-  : "a0","a1","d0","d1", "memory");
-}
-static __inline void
-Disable (void)
-{
-  extern struct ExecBase *SysBase;
-  register struct ExecBase *a6 __asm("a6") = SysBase;
-  __asm __volatile ("jsr a6@(-0x78)"
-  : /* no output */
-  : "r" (a6)
-  : "a0","a1","d0","d1", "memory");
-}
-static __inline void
-Enable (void)
-{
-  register struct ExecBase *a6 __asm("a6") = SysBase;
-  __asm __volatile ("jsr a6@(-0x7e)"
-  : /* no output */
-  : "r" (a6)
-  : "a0","a1","d0","d1", "memory");
-}
-static __inline void
-FreeMem (void * memoryBlock,unsigned long byteSize)
-{
-  register struct ExecBase *a6 __asm("a6") = SysBase;
-  register void *a1 __asm("a1") = memoryBlock;
-  register unsigned long d0 __asm("d0") = byteSize;
-  __asm __volatile ("jsr a6@(-0xd2)"
-  : /* no output */
-  : "r" (a6), "r" (a1), "r" (d0)
-  : "a0","a1","d0","d1", "memory");
-}
-static __inline struct Library *
-OpenLibrary (char *libName,unsigned long version)
-{
-  register struct Library * _res  __asm("d0");
-  register struct ExecBase *a6 __asm("a6") = SysBase;
-  register u_char *a1 __asm("a1") = libName;
-  register unsigned long d0 __asm("d0") = version;
-  __asm __volatile ("jsr a6@(-0x228)"
-  : "=r" (_res)
-  : "r" (a6), "r" (a1), "r" (d0)
-  : "a0","a1","d0","d1", "memory");
-  return _res;
-}
-static __inline void *
-SuperState (void)
-{
-  register void  *_res	__asm("d0");
-  register struct ExecBase *a6 __asm("a6") = SysBase;
-  __asm __volatile ("jsr a6@(-0x96)"
-  : "=r" (_res)
-  : "r" (a6)
-  : "a0","a1","d0","d1", "memory");
-  return _res;
-}
-static __inline void 
-CacheClearU (void)
-{
-  register struct ExecBase *a6 __asm("a6") = SysBase;
-  __asm __volatile ("jsr a6@(-0x27c)"
-  : /* no output */
-  : "r" (a6)
-  : "a0","a1","d0","d1", "memory");
-}
-static __inline unsigned long 
-CacheControl (unsigned long cacheBits,unsigned long cacheMask)
+static __inline void CloseLibrary(struct Library *library)
 {
-  register unsigned long  _res  __asm("d0");
-  register struct ExecBase *a6 __asm("a6") = SysBase;
-  register unsigned long d0 __asm("d0") = cacheBits;
-  register unsigned long d1 __asm("d1") = cacheMask;
-  __asm __volatile ("jsr a6@(-0x288)"
-  : "=r" (_res)
-  : "r" (a6), "r" (d0), "r" (d1)
-  : "a0","a1","d0","d1", "memory");
-  return _res;
+    register const struct ExecBase *a6 __asm("a6") = SysBase;
+    register struct Library *a1 __asm("a1") = library;
+    __asm __volatile ("jsr a6@(-0x19e)"
+		      : /* no output */
+		      : "r" (a6), "r" (a1)
+		      : "a0","a1","d0","d1", "memory");
 }
-static __inline unsigned long
-Supervisor (unsigned long (*userfunc)())
+
+static __inline struct Library *OpenLibrary(char *libName,
+					    unsigned long version)
 {
-  register unsigned long _res __asm("d0");
-  register struct ExecBase *a6 __asm("a6") = SysBase;
-  register unsigned long (*a0)() __asm("a0") = userfunc;
-	/* gcc doesn't seem to like asm parameters in a5 */
-  __asm __volatile ("movel a5,sp@-;movel a0,a5;jsr a6@(-0x1e);movel sp@+,a5"
-  : "=r" (_res)
-  : "r" (a6), "r" (a0)
-  : "a0","a1","d0","d1","memory");
-  return _res;
+    register struct Library * _res  __asm("d0");
+    register const struct ExecBase *a6 __asm("a6") = SysBase;
+    register u_char *a1 __asm("a1") = libName;
+    register unsigned long d0 __asm("d0") = version;
+    __asm __volatile ("jsr a6@(-0x228)"
+		      : "=r" (_res)
+		      : "r" (a6), "r" (a1), "r" (d0)
+		      : "a0","a1","d0","d1", "memory");
+    return _res;
 }
 
+static __inline char OpenDevice(u_char *devName, u_long unit,
+				struct IORequest *ioRequest, u_long flags)
+{
+    register char _res __asm("d0");
+    register const struct ExecBase *a6 __asm("a6") = SysBase;
+    register u_char *a0 __asm("a0") = devName;
+    register u_long d0 __asm("d0") = unit;
+    register struct IORequest *a1 __asm("a1") = ioRequest;
+    register u_long d1 __asm("d1") = flags;
 
-struct ExpansionBase;
-extern struct ExpansionBase *ExpansionBase;
+    __asm __volatile ("jsr a6@(-0x1bc)"
+		      : "=r" (_res)
+		      : "r" (a6), "r" (a0), "r" (a1), "r" (d0), "r" (d1)
+		      : "a0","a1","d0","d1", "memory");
+    return(_res);
+}
 
-static __inline struct ConfigDev *
-FindConfigDev (struct ConfigDev *oldConfigDev,long manufacturer,long product)
+static __inline void CloseDevice(struct IORequest *ioRequest)
 {
-  register struct ConfigDev * _res  __asm("d0");
-  register struct ExpansionBase* a6 __asm("a6") = ExpansionBase;
-  register struct ConfigDev *a0 __asm("a0") = oldConfigDev;
-  register long d0 __asm("d0") = manufacturer;
-  register long d1 __asm("d1") = product;
-  __asm __volatile ("jsr a6@(-0x48)"
-  : "=r" (_res)
-  : "r" (a6), "r" (a0), "r" (d0), "r" (d1)
-  : "a0","a1","d0","d1", "memory");
-  return _res;
+    register const struct ExecBase *a6 __asm("a6") = SysBase;
+    register struct IORequest *a1 __asm("a1") = ioRequest;
+
+    __asm __volatile ("jsr a6@(-0x1c2)"
+		      : /* no output */
+		      : "r" (a6), "r" (a1)
+		      : "a0","a1","d0","d1", "memory");
 }
 
-struct GfxBase;
-extern struct GfxBase *GfxBase;
-struct View;
-static __inline void 
-LoadView (struct View *view)
+static __inline char DoIO(struct IORequest *ioRequest)
 {
-  register struct GfxBase* a6 __asm("a6") = GfxBase;
-  register struct View *a1 __asm("a1") = view;
-  __asm __volatile ("jsr a6@(-0xde)"
-  : /* no output */
-  : "r" (a6), "r" (a1)
-  : "a0","a1","d0","d1", "memory");
+    register char _res __asm("d0");
+    register const struct ExecBase *a6 __asm("a6") = SysBase;
+    register struct IORequest *a1 __asm("a1") = ioRequest;
+
+    __asm __volatile ("jsr a6@(-0x1c8)"
+		      : "=r" (_res)
+		      : "r" (a6), "r" (a1)
+		      : "a0","a1","d0","d1", "memory");
+    return(_res);
 }
 
-static __inline void change_stack (char *stackp)
+static __inline void *CreateIORequest(struct MsgPort *port, u_long size)
 {
-    __asm__ volatile ("movel %0,sp\n\t" :: "g" (stackp) : "sp");
+    register struct Library *_res __asm("d0");
+    register const struct ExecBase *a6 __asm("a6") = SysBase;
+    register struct MsgPort *a0 __asm("a0") = port;
+    register u_long d0 __asm("d0") = size;
+
+    __asm __volatile ("jsr a6@(-0x28e)"
+		      : "=r" (_res)
+		      : "r" (a6), "r" (a0), "r" (d0)
+		      : "a0","a1","d0","d1", "memory");
+    return(_res);
 }
 
-static __inline void disable_cache (void)
+static __inline void DeleteIORequest(void *ioRequest)
 {
-    __asm__ volatile ("movec %0,cacr" :: "d" (0));
+    register const struct ExecBase *a6 __asm("a6") = SysBase;
+    register void *a0 __asm("a0") = ioRequest;
+
+    __asm __volatile ("jsr a6@(-0x294)"
+		      : /* no output */
+		      : "r" (a6), "r" (a0)
+		      : "a0","a1","d0","d1", "memory");
 }
 
-static __inline void disable_mmu (void)
+static __inline struct MsgPort *CreateMsgPort(void)
 {
-    if (SysBase->AttnFlags & AFF_68040)
-	    __asm__ volatile ("moveq #0,d0;"
-			      ".long 0x4e7b0003;"	/* movec d0,tc */
-			      ".long 0x4e7b0004;"	/* movec d0,itt0 */
-			      ".long 0x4e7b0005;"	/* movec d0,itt1 */
-			      ".long 0x4e7b0006;"	/* movec d0,dtt0 */
-			      ".long 0x4e7b0007"	/* movec d0,dtt1 */
-			      : /* no outputs */
-			      : /* no inputs */
-			      : "d0");
-    else {
-	    __asm__ volatile ("subl  #4,sp;"
-			      "pmove tc,sp@;"
-			      "bclr  #7,sp@;"
-			      "pmove sp@,tc;"
-			      "addl  #4,sp");
-	    if (SysBase->AttnFlags & AFF_68030)
-		    __asm__ volatile ("clrl  sp@-;"
-				      ".long 0xf0170800;" /* pmove sp@,tt0 */
-				      ".long 0xf0170c00;" /* pmove sp@,tt1 */
-				      "addql #4,sp");
-    }
+    register struct MsgPort *_res __asm("d0");
+    register const struct ExecBase *a6 __asm("a6") = SysBase;
+
+    __asm __volatile ("jsr a6@(-0x29a)"
+		      : "=r" (_res)
+		      : "r" (a6)
+		      : "a0","a1","d0","d1", "memory");
+    return(_res);
 }
 
-static __inline void jump_to (unsigned long addr)
+static __inline void DeleteMsgPort(struct MsgPort *port)
 {
-    __asm__ volatile ("jmp %0@" :: "a" (addr));
-    /* NOTREACHED */
-}
+    register const struct ExecBase *a6 __asm("a6") = SysBase;
+    register struct MsgPort *a0 __asm("a0") = port;
 
-#endif /* BOOTSTRAP_H */
+    __asm __volatile ("jsr a6@(-0x2a0)"
+		      : /* no output */
+		      : "r" (a6), "r" (a0)
+		      : "a0","a1","d0","d1", "memory");
+}
diff -urN pc-2.0.21/arch/m68k/boot/amiga/linuxboot.c linux-2.0.21/arch/m68k/boot/amiga/linuxboot.c
--- pc-2.0.21/arch/m68k/boot/amiga/linuxboot.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/arch/m68k/boot/amiga/linuxboot.c	Wed Sep 11 20:28:54 1996
@@ -0,0 +1,1128 @@
+/*
+ *  linux/arch/m68k/boot/amiga/linuxboot.c -- Generic routine to boot Linux/m68k
+ *					      on Amiga, used by both Amiboot and
+ *					      Amiga-Lilo.
+ *
+ *	Created 1996 by Geert Uytterhoeven
+ *
+ *
+ *  This file is based on the original bootstrap code (bootstrap.c):
+ *
+ *	Copyright (C) 1993, 1994 Hamish Macdonald
+ *				 Greg Harp
+ *
+ *		    with work by Michael Rausch
+ *				 Geert Uytterhoeven
+ *				 Frank Neumann
+ *				 Andreas Schwab
+ *
+ *
+ *  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.
+ */
+
+
+#ifndef __GNUC__
+#error GNU CC is required to compile this program
+#endif /* __GNUC__ */
+
+
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <linux/a.out.h>
+#include <linux/elf.h>
+#include <linux/linkage.h>
+#include <asm/setup.h>
+#include <asm/amigatypes.h>
+#include <asm/amigahw.h>
+#include <asm/page.h>
+
+#include "linuxboot.h"
+
+
+#undef custom
+#define custom ((*(volatile struct CUSTOM *)(CUSTOM_PHYSADDR)))
+
+/* temporary stack size */
+#define TEMP_STACKSIZE  (256)
+
+extern char copyall, copyallend;
+
+static struct exec kexec;
+static Elf32_Ehdr kexec_elf;
+static struct bootinfo bi;
+
+static const struct linuxboot_args *linuxboot_args;
+
+#define kernelname	linuxboot_args->kernelname
+#define ramdiskname	linuxboot_args->ramdiskname
+#define commandline	linuxboot_args->commandline
+#define debugflag	linuxboot_args->debugflag
+#define keep_video	linuxboot_args->keep_video
+#define reset_boards	linuxboot_args->reset_boards
+
+#define Puts		linuxboot_args->puts
+#define GetChar		linuxboot_args->getchar
+#define PutChar		linuxboot_args->putchar
+#define Printf		linuxboot_args->printf
+#define Open		linuxboot_args->open
+#define Seek		linuxboot_args->seek
+#define Read		linuxboot_args->read
+#define Close		linuxboot_args->close
+#define FileSize	linuxboot_args->filesize
+#define Sleep		linuxboot_args->sleep
+#define ModifyBootinfo	linuxboot_args->modify_bootinfo
+
+
+    /*
+     *  Function Prototypes
+     */
+
+static u_long get_chipset(void);
+static u_long get_cpu(void);
+static u_long get_model(u_long chipset);
+static int probe_resident(const char *name);
+static int probe_resource(const char *name);
+static int check_bootinfo_version(const char *memptr);
+static void start_kernel(void (*startfunc)(), char *stackp, char *memptr,
+			 u_long start_mem, u_long mem_size, u_long rd_size,
+			 u_long kernel_size) __attribute__ ((noreturn));
+asmlinkage u_long maprommed(void);
+
+
+    /*
+     *	Reset functions for nasty Zorro boards
+     */
+
+static void reset_rb3(const struct ConfigDev *cd);
+static void reset_piccolo(const struct ConfigDev *cd);
+static void reset_sd64(const struct ConfigDev *cd);
+static void reset_ariadne(const struct ConfigDev *cd);
+static void reset_hydra(const struct ConfigDev *cd);
+#if 0
+static void reset_a2060(const struct ConfigDev *cd);
+#endif
+
+struct boardreset {
+    u_short manuf;
+    u_short prod;
+    const char *name;
+    void (*reset)(const struct ConfigDev *cd);
+};
+
+static struct boardreset boardresetdb[] = {
+    { MANUF_HELFRICH1, PROD_RAINBOW3, "Rainbow 3", reset_rb3 },
+    { MANUF_HELFRICH2, PROD_PICCOLO_REG, "Piccolo", reset_piccolo },
+    { MANUF_HELFRICH2, PROD_SD64_REG, "SD64", reset_sd64 },
+    { MANUF_VILLAGE_TRONIC, PROD_ARIADNE, "Ariadne", reset_ariadne },
+    { MANUF_HYDRA_SYSTEMS, PROD_AMIGANET, "Hydra", reset_hydra },
+#if 0
+    { MANUF_COMMODORE, PROD_A2060, "A2060", reset_a2060 },
+#endif
+};
+#define NUM_BOARDRESET	sizeof(boardresetdb)/sizeof(*boardresetdb)
+
+static void (*boardresetfuncs[NUM_AUTO])(const struct ConfigDev *cd);
+
+
+const char *amiga_models[] = {
+    "Amiga 500", "Amiga 500+", "Amiga 600", "Amiga 1000", "Amiga 1200",
+    "Amiga 2000", "Amiga 2500", "Amiga 3000", "Amiga 3000T", "Amiga 3000+",
+    "Amiga 4000", "Amiga 4000T", "CDTV", "CD32", "Draco"
+};
+const u_long first_amiga_model = AMI_500;
+const u_long last_amiga_model = AMI_DRACO;
+
+
+#define MASK(model)	(1<<AMI_##model)
+
+#define CLASS_A3000	(MASK(3000) | MASK(3000T))
+#define CLASS_A4000	(MASK(4000) | MASK(4000T))
+#define CLASS_ZKICK	(MASK(500) | MASK(1000) | MASK(2000) | MASK(2500))
+
+
+    /*
+     *	Boot the Linux/m68k Operating System
+     */
+
+u_long linuxboot(const struct linuxboot_args *args)
+{
+    int kfd = -1, rfd = -1, elf_kernel = 0;
+    int i, j;
+    const struct MemHeader *mnp;
+    struct ConfigDev *cdp = NULL;
+    char *memptr = NULL;
+    u_long *stack = NULL;
+    u_long fast_total, model_mask, startcodesize, start_mem, mem_size, rd_size;
+    u_long kernel_size;
+    u_long memreq = 0, text_offset = 0;
+    Elf32_Phdr *kernel_phdrs = NULL;
+    void (*startfunc)(void);
+    u_short manuf;
+    u_char prod;
+
+    linuxboot_args = args;
+
+    /* print the greet message */
+    Puts("\nLinux/m68k Amiga Bootstrap version " AMIBOOT_VERSION "\n");
+    Puts("Copyright 1993,1994 by Hamish Macdonald and Greg Harp\n\n");
+
+    /* machine is Amiga */
+    bi.machtype = MACH_AMIGA;
+
+    /* determine chipset */
+    bi.bi_amiga.chipset = get_chipset();
+
+    /* determine CPU type */
+    bi.cputype = get_cpu();
+
+    /* determine Amiga model */
+    bi.bi_amiga.model = get_model(bi.bi_amiga.chipset);
+    model_mask = (bi.bi_amiga.model != AMI_UNKNOWN) ? 1<<bi.bi_amiga.model : 0;
+
+    /* Memory & AutoConfig based on 'unix_boot.c' by C= */
+
+    /* find all of the autoconfig boards in the system */
+    bi.bi_amiga.num_autocon = 0;
+    for (i = 0; (cdp = (struct ConfigDev *)FindConfigDev(cdp, -1, -1)); i++) {
+	if (bi.bi_amiga.num_autocon < NUM_AUTO) {
+	    /* copy the contents of each structure into our boot info */
+	    memcpy(&bi.bi_amiga.autocon[bi.bi_amiga.num_autocon], cdp,
+		   sizeof(struct ConfigDev));
+	    /* count this device */
+	    bi.bi_amiga.num_autocon++;
+	} else
+	    Printf("Warning: too many AutoConfig devices. Ignoring device at "
+		   "0x%08lx\n", cdp->cd_BoardAddr);
+    }
+
+    /* find out the memory in the system */
+    bi.num_memory = 0;
+    for (mnp = (struct MemHeader *)SysBase->MemList.lh_Head;
+	 mnp->mh_Node.ln_Succ;
+	 mnp = (struct MemHeader *)mnp->mh_Node.ln_Succ) {
+	struct MemHeader mh;
+
+	/* copy the information */
+	mh = *mnp;
+
+	/* skip virtual memory */
+	if (!(mh.mh_Attributes & MEMF_PUBLIC))
+	    continue;
+
+	/* if we suspect that Kickstart is shadowed in an A3000,
+	   modify the entry to show 512K more at the top of RAM
+	   Check first for a MapROMmed A3640 board: overwriting the
+	   Kickstart image causes an infinite lock-up on reboot! */
+	if ((mh.mh_Upper == (void *)0x07f80000) &&
+	    (model_mask & (CLASS_A3000 | CLASS_A4000)))
+	    if ((bi.cputype & CPU_68040) && Supervisor(maprommed))
+		Puts("A3640 MapROM detected.\n");
+	    else if (model_mask & CLASS_A3000) {
+		mh.mh_Upper = (void *)0x08000000;
+		Puts("A3000 shadowed Kickstart detected.\n");
+	    }
+
+	/* if we suspect that Kickstart is zkicked,
+	   modify the entry to show 512K more at the botton of RAM */
+	if ((mh.mh_Lower == (void *)0x00280020) &&
+	    (model_mask & CLASS_ZKICK)) {
+	    mh.mh_Lower = (void *)0x00200000;
+	    Puts("ZKick detected.\n");
+	}
+
+	/* mask the memory limit values */
+	mh.mh_Upper = (void *)((u_long)mh.mh_Upper & 0xfffff000);
+	mh.mh_Lower = (void *)((u_long)mh.mh_Lower & 0xfffff000);
+
+	/* if fast memory */
+	if (mh.mh_Attributes & MEMF_FAST) {
+	    /* set the size value to the size of this block and mask off to a
+	       256K increment */
+	    u_long size = ((u_long)mh.mh_Upper-(u_long)mh.mh_Lower)&0xfffc0000;
+	    if (size > 0)
+		if (bi.num_memory < NUM_MEMINFO) {
+		    /* record the start and size */
+		    bi.memory[bi.num_memory].addr = (u_long)mh.mh_Lower;
+		    bi.memory[bi.num_memory].size = size;
+		    /* count this block */
+		    bi.num_memory++;
+		} else
+		    Printf("Warning: too many memory blocks. Ignoring block "
+			   "of %ldK at 0x%08x\n", size>>10,
+			   (u_long)mh.mh_Lower);
+	} else if (mh.mh_Attributes & MEMF_CHIP)
+	    /* if CHIP memory, record the size */
+	    bi.bi_amiga.chip_size = (u_long)mh.mh_Upper;
+    }
+
+    /* get info from ExecBase */
+    bi.bi_amiga.vblank = SysBase->VBlankFrequency;
+    bi.bi_amiga.psfreq = SysBase->PowerSupplyFrequency;
+    bi.bi_amiga.eclock = SysBase->ex_EClockFrequency;
+
+    /* copy command line options into the kernel command line */
+    strncpy(bi.command_line, commandline, CL_SIZE);
+    bi.command_line[CL_SIZE-1] = '\0';
+
+
+    /* modify the bootinfo, e.g. to change the memory configuration */
+    if (ModifyBootinfo && !ModifyBootinfo(&bi))
+	goto Fail;
+
+
+    /* display Amiga model */
+    if (bi.bi_amiga.model >= first_amiga_model &&
+	bi.bi_amiga.model <= last_amiga_model)
+	Printf("%s ", amiga_models[bi.bi_amiga.model-first_amiga_model]);
+    else
+	Puts("Amiga ");
+
+    /* display the CPU type */
+    Puts("CPU: ");
+    switch (bi.cputype & CPU_MASK) {
+	case CPU_68020:
+	    Puts("68020 (Do you have an MMU?)");
+	    break;
+	case CPU_68030:
+	    Puts("68030");
+	    break;
+	case CPU_68040:
+	    Puts("68040");
+	    break;
+	case CPU_68060:
+	    Puts("68060");
+	    break;
+	default:
+	    Puts("Insufficient for Linux.  Aborting...\n");
+	    Printf("SysBase->AttnFlags = 0x%08lx\n", SysBase->AttnFlags);
+	    goto Fail;
+    }
+    switch (bi.cputype & ~CPU_MASK) {
+	case FPU_68881:
+	    Puts(" with 68881 FPU");
+	    break;
+	case FPU_68882:
+	    Puts(" with 68882 FPU");
+	    break;
+	case FPU_68040:
+	case FPU_68060:
+	    Puts(" with internal FPU");
+	    break;
+	default:
+	    Puts(" without FPU");
+	    break;
+    }
+
+    /* display the chipset */
+    switch(bi.bi_amiga.chipset) {
+	case CS_STONEAGE:
+	    Puts(", old or unknown chipset");
+	    break;
+	case CS_OCS:
+	    Puts(", OCS");
+	    break;
+	case CS_ECS:
+	    Puts(", ECS");
+	    break;
+	case CS_AGA:
+	    Puts(", AGA chipset");
+	    break;
+    }
+
+    Puts("\n\n");
+
+    /* display the command line */
+    Printf("Command line is '%s'\n", bi.command_line);
+
+    /* display the clock statistics */
+    Printf("Vertical Blank Frequency: %ldHz\n", bi.bi_amiga.vblank);
+    Printf("Power Supply Frequency: %ldHz\n", bi.bi_amiga.psfreq);
+    Printf("EClock Frequency: %ldHz\n\n", bi.bi_amiga.eclock);
+
+    /* display autoconfig devices */
+    if (bi.bi_amiga.num_autocon) {
+	Printf("Found %ld AutoConfig Device%s\n", bi.bi_amiga.num_autocon,
+	       bi.bi_amiga.num_autocon > 1 ? "s" : "");
+	for (i = 0; i < bi.bi_amiga.num_autocon; i++) {
+	    Printf("Device %ld: addr = 0x%08lx", i,
+		   (u_long)bi.bi_amiga.autocon[i].cd_BoardAddr);
+	    boardresetfuncs[i] = NULL;
+	    if (reset_boards) {
+		manuf = bi.bi_amiga.autocon[i].cd_Rom.er_Manufacturer;
+		prod = bi.bi_amiga.autocon[i].cd_Rom.er_Product;
+		for (j = 0; j < NUM_BOARDRESET; j++)
+		    if ((manuf == boardresetdb[j].manuf) &&
+			(prod == boardresetdb[j].prod)) {
+			Printf(" [%s - will be reset at kernel boot time]",
+			       boardresetdb[j].name);
+			boardresetfuncs[i] = boardresetdb[j].reset;
+			break;
+		    }
+	    }
+	    PutChar('\n');
+	}
+    } else
+	Puts("No AutoConfig Devices Found\n");
+
+    /* display memory */
+    if (bi.num_memory) {
+	Printf("\nFound %ld Block%sof Memory\n", bi.num_memory,
+	       bi.num_memory > 1 ? "s " : " ");
+	for (i = 0; i < bi.num_memory; i++)
+	    Printf("Block %ld: 0x%08lx to 0x%08lx (%ldK)\n", i,
+		   bi.memory[i].addr, bi.memory[i].addr+bi.memory[i].size,
+		   bi.memory[i].size>>10);
+    } else {
+	Puts("No memory found?!  Aborting...\n");
+	goto Fail;
+    }
+
+    /* display chip memory size */
+    Printf("%ldK of CHIP memory\n", bi.bi_amiga.chip_size>>10);
+
+    start_mem = bi.memory[0].addr;
+    mem_size = bi.memory[0].size;
+
+    /* tell us where the kernel will go */
+    Printf("\nThe kernel will be located at 0x%08lx\n", start_mem);
+
+    /* verify that there is enough Chip RAM */
+    if (bi.bi_amiga.chip_size < 512*1024) {
+	Puts("Not enough Chip RAM in this system.  Aborting...\n");
+	goto Fail;
+    }
+
+    /* verify that there is enough Fast RAM */
+    for (fast_total = 0, i = 0; i < bi.num_memory; i++)
+	fast_total += bi.memory[i].size;
+    if (fast_total < 2*1024*1024) {
+	Puts("Not enough Fast RAM in this system.  Aborting...\n");
+	goto Fail;
+    }
+
+    /* support for ramdisk */
+    if (ramdiskname) {
+	int size;
+
+	if ((size = FileSize(ramdiskname)) == -1) {
+	    Printf("Unable to find size of ramdisk file `%s'\n", ramdiskname);
+	    goto Fail;
+	}
+	/* record ramdisk size */
+	bi.ramdisk_size = (size+1023)>>10;
+    } else
+	bi.ramdisk_size = 0;
+    rd_size = bi.ramdisk_size<<10;
+    bi.ramdisk_addr = start_mem+mem_size-rd_size;
+
+    /* open kernel executable and read exec header */
+    if ((kfd = Open(kernelname)) == -1) {
+	Printf("Unable to open kernel file `%s'\n", kernelname);
+	goto Fail;
+    }
+    if (Read(kfd, (void *)&kexec, sizeof(kexec)) != sizeof(kexec)) {
+	Puts("Unable to read exec header from kernel file\n");
+	goto Fail;
+    }
+
+    switch (N_MAGIC(kexec)) {
+	case ZMAGIC:
+	    if (debugflag)
+		Puts("\nLoading a.out (ZMAGIC) Linux/m68k kernel...\n");
+	    text_offset = N_TXTOFF(kexec);
+	    break;
+
+	case QMAGIC:
+	    if (debugflag)
+		Puts("\nLoading a.out (QMAGIC) Linux/m68k kernel...\n");
+	    text_offset = sizeof(kexec);
+	    /* the text size includes the exec header; remove this */
+	    kexec.a_text -= sizeof(kexec);
+	    break;
+
+	default:
+	    /* Try to parse it as an ELF header */
+	    Seek(kfd, 0);
+	    if ((Read(kfd, (void *)&kexec_elf, sizeof(kexec_elf)) ==
+		 sizeof(kexec_elf)) &&
+		 (memcmp(&kexec_elf.e_ident[EI_MAG0], ELFMAG, SELFMAG) == 0)) {
+		elf_kernel = 1;
+		if (debugflag)
+		    Puts("\nLoading ELF Linux/m68k kernel...\n");
+		/* A few plausibility checks */
+		if ((kexec_elf.e_type != ET_EXEC) ||
+		    (kexec_elf.e_machine != EM_68K) ||
+		    (kexec_elf.e_version != EV_CURRENT)) {
+		    Puts("Invalid ELF header contents in kernel\n");
+		    goto Fail;
+		}
+		/* Load the program headers */
+		if (!(kernel_phdrs =
+		      (Elf32_Phdr *)AllocMem(kexec_elf.e_phnum*sizeof(Elf32_Phdr),
+					     MEMF_FAST | MEMF_PUBLIC |
+					     MEMF_CLEAR))) {
+		    Puts("Unable to allocate memory for program headers\n");
+		    goto Fail;
+		}
+		Seek(kfd, kexec_elf.e_phoff);
+		if (Read(kfd, (void *)kernel_phdrs,
+			 kexec_elf.e_phnum*sizeof(*kernel_phdrs)) !=
+		    kexec_elf.e_phnum*sizeof(*kernel_phdrs)) {
+		    Puts("Unable to read program headers from kernel file\n");
+		    goto Fail;
+		}
+		break;
+	    }
+	    Printf("Wrong magic number 0x%08lx in kernel header\n",
+		   N_MAGIC(kexec));
+	    goto Fail;
+    }
+
+    /* Load the kernel at one page after start of mem */
+    start_mem += PAGE_SIZE;
+    mem_size -= PAGE_SIZE;
+    /* Align bss size to multiple of four */
+    if (!elf_kernel)
+	kexec.a_bss = (kexec.a_bss+3) & ~3;
+
+    /* calculate the total required amount of memory */
+    if (elf_kernel) {
+	u_long min_addr = 0xffffffff, max_addr = 0;
+	for (i = 0; i < kexec_elf.e_phnum; i++) {
+	    if (min_addr > kernel_phdrs[i].p_vaddr)
+		min_addr = kernel_phdrs[i].p_vaddr;
+	    if (max_addr < kernel_phdrs[i].p_vaddr+kernel_phdrs[i].p_memsz)
+		max_addr = kernel_phdrs[i].p_vaddr+kernel_phdrs[i].p_memsz;
+	}
+	/* This is needed for newer linkers that include the header in
+	   the first segment.  */
+	if (min_addr == 0) {
+	    min_addr = PAGE_SIZE;
+	    kernel_phdrs[0].p_vaddr += PAGE_SIZE;
+	    kernel_phdrs[0].p_offset += PAGE_SIZE;
+	    kernel_phdrs[0].p_filesz -= PAGE_SIZE;
+	    kernel_phdrs[0].p_memsz -= PAGE_SIZE;
+	}
+	kernel_size = max_addr-min_addr;
+    } else
+	kernel_size = kexec.a_text+kexec.a_data+kexec.a_bss;
+    memreq = kernel_size+sizeof(struct bootinfo)+rd_size;
+    if (!(memptr = (char *)AllocMem(memreq, MEMF_FAST | MEMF_PUBLIC |
+					    MEMF_CLEAR))) {
+	Puts("Unable to allocate memory\n");
+	goto Fail;
+    }
+
+    /* read the text and data segments from the kernel image */
+    if (elf_kernel)
+	for (i = 0; i < kexec_elf.e_phnum; i++) {
+	    if (Seek(kfd, kernel_phdrs[i].p_offset) == -1) {
+		Printf("Failed to seek to segment %ld\n", i);
+		goto Fail;
+	    }
+	    if (Read(kfd, memptr+kernel_phdrs[i].p_vaddr-PAGE_SIZE,
+		     kernel_phdrs[i].p_filesz) != kernel_phdrs[i].p_filesz) {
+		Printf("Failed to read segment %ld\n", i);
+		goto Fail;
+	    }
+	}
+    else {
+	if (Seek(kfd, text_offset) == -1) {
+	    Printf("Failed to seek to text\n");
+	    goto Fail;
+	}
+	if (Read(kfd, memptr, kexec.a_text) != kexec.a_text) {
+	    Printf("Failed to read text\n");
+	    goto Fail;
+	}
+	/* data follows immediately after text */
+	if (Read(kfd, memptr+kexec.a_text, kexec.a_data) != kexec.a_data) {
+	    Printf("Failed to read data\n");
+	    goto Fail;
+	}
+    }
+    Close(kfd);
+    kfd = -1;
+
+    /* Check kernel's bootinfo version */
+    if (!check_bootinfo_version(memptr))
+	goto Fail;
+
+    /* copy the bootinfo to the end of the kernel image */
+    memcpy((void *)(memptr+kernel_size), &bi, sizeof(struct bootinfo));
+
+    if (ramdiskname) {
+	if ((rfd = Open(ramdiskname)) == -1) {
+	    Printf("Unable to open ramdisk file `%s'\n", ramdiskname);
+	    goto Fail;
+	}
+	if (Read(rfd, memptr+kernel_size+sizeof(bi), rd_size) != rd_size) {
+	    Printf("Failed to read ramdisk file\n");
+	    goto Fail;
+	}
+	Close(rfd);
+	rfd = -1;
+    }
+
+    /* allocate temporary chip ram stack */
+    if (!(stack = (u_long *)AllocMem(TEMP_STACKSIZE, MEMF_CHIP | MEMF_CLEAR))) {
+	Puts("Unable to allocate memory for stack\n");
+	goto Fail;
+    }
+
+    /* allocate chip ram for copy of startup code */
+    startcodesize = &copyallend-&copyall;
+    if (!(startfunc = (void (*)(void))AllocMem(startcodesize,
+					       MEMF_CHIP | MEMF_CLEAR))) {
+	Puts("Unable to allocate memory for startcode\n");
+	goto Fail;
+    }
+
+    /* copy startup code to CHIP RAM */
+    memcpy(startfunc, &copyall, startcodesize);
+
+    if (debugflag) {
+	if (bi.ramdisk_size)
+	    Printf("RAM disk at 0x%08lx, size is %ldK\n",
+		   (u_long)memptr+kernel_size, bi.ramdisk_size);
+
+	if (elf_kernel) {
+	    PutChar('\n');
+	    for (i = 0; i < kexec_elf.e_phnum; i++)
+		Printf("Kernel segment %ld at 0x%08lx, size %ld\n", i,
+		       start_mem+kernel_phdrs[i].p_vaddr-PAGE_SIZE,
+		       kernel_phdrs[i].p_memsz);
+	    Printf("Boot info        at 0x%08lx\n", start_mem+kernel_size);
+	} else {
+	    Printf("\nKernel text at 0x%08lx, code size 0x%08lx\n", start_mem,
+		   kexec.a_text);
+	    Printf("Kernel data at 0x%08lx, data size 0x%08lx\n",
+		   start_mem+kexec.a_text, kexec.a_data);
+	    Printf("Kernel bss  at 0x%08lx, bss  size 0x%08lx\n",
+		   start_mem+kexec.a_text+kexec.a_data, kexec.a_bss);
+	    Printf("Boot info   at 0x%08lx\n", start_mem+kernel_size);
+	}
+	Printf("\nKernel entry is 0x%08lx\n", elf_kernel ? kexec_elf.e_entry :
+							   kexec.a_entry);
+
+	Printf("ramdisk dest top is 0x%08lx\n", start_mem+mem_size);
+	Printf("ramdisk lower limit is 0x%08lx\n", (u_long)memptr+kernel_size);
+	Printf("ramdisk src top is 0x%08lx\n",
+	       (u_long)memptr+kernel_size+rd_size);
+
+	Puts("\nType a key to continue the Linux/m68k boot...");
+	GetChar();
+	PutChar('\n');
+    }
+
+    /* wait for things to settle down */
+    Sleep(1000000);
+
+    if (!keep_video)
+	/* set graphics mode to a nice normal one */
+	LoadView(NULL);
+
+    Disable();
+
+    /* reset nasty Zorro boards */
+    if (reset_boards)
+	for (i = 0; i < bi.bi_amiga.num_autocon; i++)
+	    if (boardresetfuncs[i])
+		boardresetfuncs[i](&bi.bi_amiga.autocon[i]);
+
+    /* Turn off all DMA */
+    custom.dmacon = DMAF_ALL | DMAF_MASTER;
+
+    /* turn off caches */
+    CacheControl(0, ~0);
+
+    /* Go into supervisor state */
+    SuperState();
+
+    /* turn off any mmu translation */
+    disable_mmu();
+
+    /* execute the copy-and-go code (from CHIP RAM) */
+    start_kernel(startfunc, (char *)stack+TEMP_STACKSIZE, memptr, start_mem,
+		 mem_size, rd_size, kernel_size);
+
+    /* Clean up and exit in case of a failure */
+Fail:
+    if (kfd != -1)
+	Close(kfd);
+    if (rfd != -1)
+	Close(rfd);
+    if (memptr)
+	FreeMem((void *)memptr, memreq);
+    if (stack)
+	FreeMem((void *)stack, TEMP_STACKSIZE);
+    if (kernel_phdrs)
+	FreeMem((void *)kernel_phdrs, kexec_elf.e_phnum*sizeof(Elf32_Phdr));
+    return(FALSE);
+}
+
+
+    /*
+     *	Determine the Chipset
+     */
+
+static u_long get_chipset(void)
+{
+    u_char cs;
+    u_long chipset;
+
+    if (GfxBase->Version >= 39)
+	cs = SetChipRev(SETCHIPREV_BEST);
+    else
+	cs = GfxBase->ChipRevBits0;
+    if ((cs & GFXG_AGA) == GFXG_AGA)
+	chipset = CS_AGA;
+    else if ((cs & GFXG_ECS) == GFXG_ECS)
+	chipset = CS_ECS;
+    else if ((cs & GFXG_OCS) == GFXG_OCS)
+	chipset = CS_OCS;
+    else
+	chipset = CS_STONEAGE;
+    return(chipset);
+}
+
+
+    /*
+     *	Determine the CPU Type
+     */
+
+static u_long get_cpu(void)
+{
+    u_long cpu = 0;
+
+    if (SysBase->AttnFlags & AFF_68060) {
+	cpu = CPU_68060;
+	if (SysBase->AttnFlags & AFF_FPU40)
+	    cpu |= FPU_68060;
+    } else if (SysBase->AttnFlags & AFF_68040) {
+	cpu = CPU_68040;
+	if (SysBase->AttnFlags & AFF_FPU40)
+	    cpu |= FPU_68040;
+    } else {
+	if (SysBase->AttnFlags & AFF_68030)
+	    cpu = CPU_68030;
+	else if (SysBase->AttnFlags & AFF_68020)
+	    cpu = CPU_68020;
+	if (SysBase->AttnFlags & AFF_68882)
+	    cpu |= FPU_68882;
+	else if (SysBase->AttnFlags & AFF_68881)
+	    cpu |= FPU_68881;
+    }
+    return(cpu);
+}
+
+
+    /*
+     *	Determine the Amiga Model
+     */
+
+static u_long get_model(u_long chipset)
+{
+    u_long model = AMI_UNKNOWN;
+
+    if (debugflag)
+	Puts("Amiga model identification:\n");
+    if (probe_resource("draco.resource"))
+	model = AMI_DRACO;
+    else {
+	if (debugflag)
+	    Puts("    Chipset: ");
+	switch(chipset) {
+	    case CS_STONEAGE:
+		if (debugflag)
+		    Puts("Old or unknown\n");
+		goto OCS;
+		break;
+
+	    case CS_OCS:
+		if (debugflag)
+		    Puts("OCS\n");
+OCS:		if (probe_resident("cd.device"))
+		    model = AMI_CDTV;
+		else
+		    /* let's call it an A2000 (may be A500, A1000, A2500) */
+		    model = AMI_2000;
+		break;
+
+	    case CS_ECS:
+		if (debugflag)
+		    Puts("ECS\n");
+		if (probe_resident("Magic 36.7") ||
+		    probe_resident("kickad 36.57") ||
+		    probe_resident("A3000 Bonus") ||
+		    probe_resident("A3000 bonus"))
+		    /* let's call it an A3000 (may be A3000T) */
+		    model = AMI_3000;
+		else if (probe_resource("card.resource"))
+		    model = AMI_600;
+		else
+		    /* let's call it an A2000 (may be A500[+], A1000, A2500) */
+		    model = AMI_2000;
+		break;
+
+	    case CS_AGA:
+		if (debugflag)
+		    Puts("AGA\n");
+		if (probe_resident("A1000 Bonus") ||
+		    probe_resident("A4000 bonus"))
+		    model = probe_resident("NCR scsi.device") ? AMI_4000T :
+								AMI_4000;
+		else if (probe_resource("card.resource"))
+		    model = AMI_1200;
+		else if (probe_resident("cd.device"))
+		    model = AMI_CD32;
+		else
+		    model = AMI_3000PLUS;
+		break;
+	}
+    }
+    if (debugflag) {
+	Puts("\nType a key to continue...");
+	GetChar();
+	Puts("\n\n");
+    }
+    return(model);
+}
+
+
+    /*
+     *	Probe for a Resident Modules
+     */
+
+static int probe_resident(const char *name)
+{
+    const struct Resident *res;
+
+    if (debugflag)
+	Printf("    Module `%s': ", name);
+    res = FindResident(name);
+    if (debugflag)
+	if (res)
+	    Printf("0x%08lx\n", res);
+	else
+	    Printf("not present\n");
+    return(res ? TRUE : FALSE);
+}
+
+
+    /*
+     *	Probe for an available Resource
+     */
+
+static int probe_resource(const char *name)
+{
+    const void *res;
+
+    if (debugflag)
+	Printf("    Resource `%s': ", name);
+    res = OpenResource(name);
+    if (debugflag)
+	if (res)
+	    Printf("0x%08lx\n", res);
+	else
+	    Printf("not present\n");
+    return(res ? TRUE : FALSE);
+}
+
+
+    /*
+     *  Compare the Bootstrap and Kernel Versions
+     */
+
+static int check_bootinfo_version(const char *memptr)
+{
+    const struct bootversion *bv = (struct bootversion *)memptr;
+    unsigned long version = 0;
+    int i, kernel_major, kernel_minor, boots_major, boots_minor;
+
+    if (bv->magic == BOOTINFOV_MAGIC)
+	for (i = 0; bv->machversions[i].machtype != 0; ++i)
+	    if (bv->machversions[i].machtype == MACH_AMIGA) {
+		version = bv->machversions[i].version;
+		break;
+	    }
+    if (!version)
+	Printf("Kernel has no bootinfo version info, assuming 0.0\n");
+
+    kernel_major = BI_VERSION_MAJOR(version);
+    kernel_minor = BI_VERSION_MINOR(version);
+    boots_major  = BI_VERSION_MAJOR(AMIGA_BOOTI_VERSION);
+    boots_minor  = BI_VERSION_MINOR(AMIGA_BOOTI_VERSION);
+    Printf("Bootstrap's bootinfo version: %ld.%ld\n", boots_major,
+	   boots_minor);
+    Printf("Kernel's bootinfo version   : %ld.%ld\n", kernel_major,
+	   kernel_minor);
+
+    if (kernel_major != boots_major) {
+	Printf("\nThis bootstrap is too %s for this kernel!\n",
+	       boots_major < kernel_major ? "old" : "new");
+	return(0);
+    }
+    if (kernel_minor > boots_minor) {
+	Printf("Warning: Bootinfo version of bootstrap and kernel differ!\n" );
+	Printf("         Certain features may not work.\n");
+    }
+    return(1);
+}
+
+
+    /*
+     *	Call the copy-and-go-code
+     */
+
+static void start_kernel(void (*startfunc)(), char *stackp, char *memptr,
+			 u_long start_mem, u_long mem_size, u_long rd_size,
+			 u_long kernel_size)
+{
+    register void (*a0)() __asm("a0") = startfunc;
+    register char *a2 __asm("a2") = stackp;
+    register char *a3 __asm("a3") = memptr;
+    register u_long a4 __asm("a4") = start_mem;
+    register u_long d0 __asm("d0") = mem_size;
+    register u_long d1 __asm("d1") = rd_size;
+    register u_long d2 __asm("d2") = kernel_size;
+    register u_long d3 __asm("d3") = sizeof(struct bootinfo);
+
+    __asm __volatile ("movel a2,sp;"
+		      "jmp a0@"
+		      : /* no outputs */
+		      : "r" (a0), "r" (a2), "r" (a3), "r" (a4), "r" (d0),
+			"r" (d1), "r" (d2), "r" (d3)
+		      /* no return */);
+    /* fake a noreturn */
+    for (;;);
+}
+
+
+    /*
+     *	This assembler code is copied to chip ram, and then executed.
+     *	It copies the kernel to it's final resting place.
+     *
+     *	It is called with:
+     *
+     *	    a3 = memptr
+     *	    a4 = start_mem
+     *	    d0 = mem_size
+     *	    d1 = rd_size
+     *	    d2 = kernel_size
+     *	    d3 = sizeof(struct bootinfo)
+     */
+
+asm(".text\n"
+ALIGN_STR "\n"
+SYMBOL_NAME_STR(copyall) ":
+				| /* copy kernel text and data */
+	movel	a3,a0		| src = (u_long *)memptr;
+	movel	a0,a2		| limit = (u_long *)(memptr+kernel_size);
+	addl	d2,a2
+	movel	a4,a1		| dest = (u_long *)start_mem;
+1:	cmpl	a0,a2
+	jeq	2f		| while (src < limit)
+	moveb	a0@+,a1@+	|  *dest++ = *src++;
+	jra	1b
+2:
+				| /* copy early bootinfo to end of bss */
+	movel	a3,a0		| src = (u_long *)(memptr+kernel_size);
+	addl	d2,a0		| dest = end of bss (already in a1)
+	movel	d3,d7		| count = sizeof(struct bootinfo)
+	subql	#1,d7
+1:	moveb	a0@+,a1@+	| while (--count > -1)
+	dbra	d7,1b		|     *dest++ = *src++
+
+				| /* copy the ramdisk to the top of memory */
+				| /* (from back to front) */
+	movel	a4,a1		| dest = (u_long *)(start_mem+mem_size);
+	addl	d0,a1
+	movel	a3,a2		| limit = (u_long *)(memptr+kernel_size +
+	addl	d2,a2		|		     sizeof(struct bootinfo));
+	addl	d3,a2
+	movel	a2,a0		| src = (u_long *)((u_long)limit+rd_size);
+	addl	d1,a0
+1:	cmpl	a0,a2
+	beqs	2f		| while (src > limit)
+	moveb	a0@-,a1@-	|     *--dest = *--src;
+	bras	1b
+2:
+				| /* jump to start of kernel */
+	movel	a4,a0		| jump_to (start_mem);
+	jmp	a0@
+"
+SYMBOL_NAME_STR(copyallend) ":
+");
+
+
+    /*
+     *	Test for a MapROMmed A3640 Board
+     */
+
+asm(".text\n"
+ALIGN_STR "\n"
+SYMBOL_NAME_STR(maprommed) ":
+	oriw	#0x0700,sr
+	moveml	#0x3f20,sp@-
+				| /* Save cache settings */
+	.long	0x4e7a1002	| movec cacr,d1 */
+				| /* Save MMU settings */
+	.long	0x4e7a2003	| movec tc,d2
+	.long	0x4e7a3004	| movec itt0,d3
+	.long	0x4e7a4005	| movec itt1,d4
+	.long	0x4e7a5006	| movec dtt0,d5
+	.long	0x4e7a6007	| movec dtt1,d6
+	moveq	#0,d0
+	movel	d0,a2
+				| /* Disable caches */
+	.long	0x4e7b0002	| movec d0,cacr
+				| /* Disable MMU */
+	.long	0x4e7b0003	| movec d0,tc
+	.long	0x4e7b0004	| movec d0,itt0
+	.long	0x4e7b0005	| movec d0,itt1
+	.long	0x4e7b0006	| movec d0,dtt0
+	.long	0x4e7b0007	| movec d0,dtt1
+	lea	0x07f80000,a0
+	lea	0x00f80000,a1
+	movel	a0@,d7
+	cmpl	a1@,d7
+	jne	1f
+	movel	d7,d0
+	notl	d0
+	movel	d0,a0@
+	nop			| /* Thanks to Jörg Mayer! */
+	cmpl	a1@,d0
+	jne	1f
+	moveq	#-1,d0		| /* MapROMmed A3640 present */
+	movel	d0,a2
+1:	movel	d7,a0@
+				| /* Restore MMU settings */
+	.long	0x4e7b2003	| movec d2,tc
+	.long	0x4e7b3004	| movec d3,itt0
+	.long	0x4e7b4005	| movec d4,itt1
+	.long	0x4e7b5006	| movec d5,dtt0
+	.long	0x4e7b6007	| movec d6,dtt1
+				| /* Restore cache settings */
+	.long	0x4e7b1002	| movec d1,cacr
+	movel	a2,d0
+	moveml	sp@+,#0x04fc
+	rte
+");
+
+
+    /*
+     *	Reset functions for nasty Zorro boards
+     */
+
+static void reset_rb3(const struct ConfigDev *cd)
+{
+    volatile u_char *rb3_reg = (u_char *)(cd->cd_BoardAddr+0x01002000);
+
+    /* FN: If a Rainbow III board is present, reset it to disable */
+    /* its (possibly activated) vertical blank interrupts as the */
+    /* kernel is not yet prepared to handle them (level 6). */
+
+    /* set RESET bit in special function register */
+    *rb3_reg = 0x01;
+    /* actually, only a few cycles delay are required... */
+    Sleep(1000000);
+    /* clear reset bit */
+    *rb3_reg = 0x00;
+}
+
+static void reset_piccolo(const struct ConfigDev *cd)
+{
+    volatile u_char *piccolo_reg = (u_char *)(cd->cd_BoardAddr+0x8000);
+
+    /* FN: the same stuff as above, for the Piccolo board. */
+    /* this also has the side effect of resetting the board's */
+    /* output selection logic to use the Amiga's display in single */
+    /* monitor systems - which is currently what we want. */
+
+    /* set RESET bit in special function register */
+    *piccolo_reg = 0x01;
+    /* actually, only a few cycles delay are required... */
+    Sleep(1000000);
+    /* clear reset bit */
+    *piccolo_reg = 0x51;
+}
+
+static void reset_sd64(const struct ConfigDev *cd)
+{
+    volatile u_char *sd64_reg = (u_char *)(cd->cd_BoardAddr+0x8000);
+
+    /* FN: the same stuff as above, for the SD64 board. */
+    /* just as on the Piccolo, this also resets the monitor switch */
+
+    /* set RESET bit in special function register */
+    *sd64_reg = 0x1f;
+    /* actually, only a few cycles delay are required... */
+    Sleep(1000000);
+    /* clear reset bit AND switch monitor bit (0x20) */
+    *sd64_reg = 0x4f;
+}
+
+static void reset_ariadne(const struct ConfigDev *cd)
+{
+    volatile u_short *lance_rdp = (u_short *)(cd->cd_BoardAddr+0x0370);
+    volatile u_short *lance_rap = (u_short *)(cd->cd_BoardAddr+0x0372);
+    volatile u_short *lance_reset = (u_short *)(cd->cd_BoardAddr+0x0374);
+
+    volatile u_char *pit_paddr = (u_char *)(cd->cd_BoardAddr+0x1004);
+    volatile u_char *pit_pbddr = (u_char *)(cd->cd_BoardAddr+0x1006);
+    volatile u_char *pit_pacr = (u_char *)(cd->cd_BoardAddr+0x100b);
+    volatile u_char *pit_pbcr = (u_char *)(cd->cd_BoardAddr+0x100e);
+    volatile u_char *pit_psr = (u_char *)(cd->cd_BoardAddr+0x101a);
+
+    u_short in;
+
+    Disable();
+
+    /*
+     *	Reset the Ethernet part (Am79C960 PCnet-ISA)
+     */
+
+    in = *lance_reset;   /* Reset Chip on Read Access */
+    *lance_rap = 0x0000; /* PCnet-ISA Controller Status (CSR0) */
+    *lance_rdp = 0x0400; /* STOP */
+
+    /*
+     *	Reset the Parallel part (MC68230 PI/T)
+     */
+
+    *pit_pacr &= 0xfd;   /* Port A Control Register */
+    *pit_pbcr &= 0xfd;   /* Port B Control Register */
+    *pit_psr = 0x05;     /* Port Status Register */
+    *pit_paddr = 0x00;   /* Port A Data Direction Register */
+    *pit_pbddr = 0x00;   /* Port B Data Direction Register */
+
+    Enable();
+}
+
+static void reset_hydra(const struct ConfigDev *cd)
+{
+    volatile u_char *nic_cr  = (u_char *)(cd->cd_BoardAddr+0xffe1);
+    volatile u_char *nic_isr = (u_char *)(cd->cd_BoardAddr+0xffe1 + 14);
+    int n = 5000;
+
+    Disable();
+ 
+    *nic_cr = 0x21;	/* nic command register: software reset etc. */
+    while(((*nic_isr & 0x80) == 0) && --n)  /* wait for reset to complete */
+	;
+ 
+    Enable();
+}
+
+#if 0
+static void reset_a2060(const struct ConfigDev *cd)
+{
+#error reset_a2060: not yet implemented
+}
+#endif
diff -urN pc-2.0.21/arch/m68k/boot/amiga/linuxboot.h linux-2.0.21/arch/m68k/boot/amiga/linuxboot.h
--- pc-2.0.21/arch/m68k/boot/amiga/linuxboot.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/arch/m68k/boot/amiga/linuxboot.h	Wed Sep 11 20:28:55 1996
@@ -0,0 +1,427 @@
+/*
+ *  linux/arch/m68k/boot/amiga/linuxboot.h -- Generic routine to boot Linux/m68k
+ *					      on Amiga, used by both Amiboot and
+ *					      Amiga-Lilo.
+ *
+ *	Created 1996 by Geert Uytterhoeven
+ *
+ *
+ *  This file is based on the original bootstrap code (bootstrap.c):
+ *
+ *	Copyright (C) 1993, 1994 Hamish Macdonald
+ *				 Greg Harp
+ *
+ *		    with work by Michael Rausch
+ *				 Geert Uytterhoeven
+ *				 Frank Neumann
+ *				 Andreas Schwab
+ *
+ *
+ *  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 <asm/setup.h>
+#include <asm/zorro.h>
+
+
+    /*
+     *  Amiboot Version
+     */
+
+#define AMIBOOT_VERSION		"4.0"
+
+
+    /*
+     *  Parameters passed to linuxboot()
+     */
+
+struct linuxboot_args {
+    const char *kernelname;
+    const char *ramdiskname;
+    const char *commandline;
+    int debugflag;
+    int keep_video;
+    int reset_boards;
+    void (*puts)(const char *str);
+    long (*getchar)(void);
+    void (*putchar)(char c);
+    void (*printf)(const char *fmt, ...);
+    int (*open)(const char *path);
+    int (*seek)(int fd, int offset);
+    int (*read)(int fd, char *buf, int count);
+    void (*close)(int fd);
+    int (*filesize)(const char *path);
+    void (*sleep)(u_long micros);
+    int (*modify_bootinfo)(struct bootinfo *bi);
+};
+
+
+    /*
+     *  Boot the Linux/m68k Operating System
+     */
+
+extern u_long linuxboot(const struct linuxboot_args *args);
+
+
+    /*
+     *  Amiga Models
+     */
+
+extern const char *amiga_models[];
+extern const u_long first_amiga_model;
+extern const u_long last_amiga_model;
+
+
+    /*
+     *	Exec Library Definitions
+     */
+
+#define TRUE	(1)
+#define FALSE	(0)
+
+
+struct List {
+    struct Node *lh_Head;
+    struct Node *lh_Tail;
+    struct Node *lh_TailPred;
+    u_char lh_Type;
+    u_char l_pad;
+};
+
+struct MemChunk {
+     struct MemChunk *mc_Next;	/* pointer to next chunk */
+     u_long mc_Bytes;		/* chunk byte size    */
+};
+
+#define MEMF_PUBLIC	(1<<0)
+#define MEMF_CHIP	(1<<1)
+#define MEMF_FAST	(1<<2)
+#define MEMF_LOCAL	(1<<8)
+#define MEMF_CLEAR	(1<<16)
+
+struct MemHeader {
+    struct Node mh_Node;
+    u_short mh_Attributes;	/* characteristics of this region */
+    struct MemChunk *mh_First;	/* first free region */
+    void *mh_Lower;		/* lower memory bound */
+    void *mh_Upper;		/* upper memory bound+1 */
+    u_long mh_Free;		/* total number of free bytes */
+};
+
+struct ExecBase {
+    u_char fill1[20];
+    u_short Version;
+    u_char fill2[274];
+    u_short AttnFlags;
+    u_char fill3[24];
+    struct List MemList;
+    u_char fill4[194];
+    u_char VBlankFrequency;
+    u_char PowerSupplyFrequency;
+    u_char fill5[36];
+    u_long ex_EClockFrequency;
+    u_char fill6[60];
+};
+
+#define AFB_68020	(1)
+#define AFF_68020	(1<<AFB_68020)
+#define AFB_68030	(2)
+#define AFF_68030	(1<<AFB_68030)
+#define AFB_68040	(3)
+#define AFF_68040	(1<<AFB_68040)
+#define AFB_68881	(4)
+#define AFF_68881	(1<<AFB_68881)
+#define AFB_68882	(5)
+#define AFF_68882	(1<<AFB_68882)
+#define AFB_FPU40	(6)		/* ONLY valid if AFB_68040 or AFB_68060 */
+#define AFF_FPU40	(1<<AFB_FPU40)	/* is set; also set for 68060 FPU */
+#define AFB_68060	(7)
+#define AFF_68060	(1<<AFB_68060)
+
+struct Resident;
+
+
+    /*
+     *	Graphics Library Definitions
+     */
+
+struct GfxBase {
+    u_char fill1[20];
+    u_short Version;
+    u_char fill2[194];
+    u_short NormalDisplayRows;
+    u_short NormalDisplayColumns;
+    u_char fill3[16];
+    u_char ChipRevBits0;
+    u_char fill4[307];
+};
+
+#define GFXB_HR_AGNUS	(0)
+#define GFXF_HR_AGNUS	(1<<GFXB_HR_AGNUS)
+#define GFXB_HR_DENISE	(1)
+#define GFXF_HR_DENISE	(1<<GFXB_HR_DENISE)
+#define GFXB_AA_ALICE	(2)
+#define GFXF_AA_ALICE	(1<<GFXB_AA_ALICE)
+#define GFXB_AA_LISA	(3)
+#define GFXF_AA_LISA	(1<<GFXB_AA_LISA)
+
+    /*
+     *	HiRes(=Big) Agnus present; i.e. 
+     *	1MB chipmem, big blits (none of interest so far) and programmable sync
+     */
+#define GFXG_OCS	(GFXF_HR_AGNUS)
+    /*
+     *	HiRes Agnus/Denise present; we are running on ECS
+     */
+#define GFXG_ECS	(GFXF_HR_AGNUS|GFXF_HR_DENISE)
+    /*
+     *	Alice and Lisa present; we are running on AGA
+     */
+#define GFXG_AGA	(GFXF_AA_ALICE|GFXF_AA_LISA)
+
+#define SETCHIPREV_BEST	(0xffffffff)
+#define HIRES		(0x8000)
+
+struct View;
+
+
+    /*
+     *	Amiga Shared Library/Device Functions
+     */
+
+extern const struct ExecBase *SysBase;
+
+#define LVOAllocMem		(-0xc6)
+#define LVOAllocVec		(-0x2ac)
+#define LVOCacheControl		(-0x288)
+#define LVODisable		(-0x78)
+#define LVOEnable		(-0x7e)
+#define LVOFindResident		(-0x60)
+#define LVOFreeMem		(-0xd2)
+#define LVOFreeVec		(-0x2b2)
+#define LVOOpenresource		(-0x1f2)
+#define LVOSuperState		(-0x96)
+#define LVOSupervisor		(-0x1e)
+
+static __inline void *AllocMem(u_long byteSize, u_long requirements)
+{
+    register void *_res __asm("d0");
+    register const struct ExecBase *_base __asm("a6") = SysBase;
+    register u_long d0 __asm("d0") = byteSize;
+    register u_long d1 __asm("d1") = requirements;
+
+    __asm __volatile ("jsr a6@(-0xc6)"
+		      : "=r" (_res)
+		      : "r" (_base), "r" (d0), "r" (d1)
+		      : "a0", "a1", "d0", "d1", "memory");
+    return(_res);
+}
+
+static __inline void *AllocVec(u_long byteSize, u_long requirements)
+{
+    register void *_res __asm("d0");
+    register const struct ExecBase *_base __asm("a6") = SysBase;
+    register u_long d0 __asm("d0") = byteSize;
+    register u_long d1 __asm("d1") = requirements;
+
+    __asm __volatile ("jsr a6@(-0x2ac)"
+		      : "=r" (_res)
+		      : "r" (_base), "r" (d0), "r" (d1)
+		      : "a0", "a1", "d0", "d1", "memory");
+    return(_res);
+}
+
+static __inline u_long CacheControl(u_long cacheBits, u_long cacheMask)
+{
+    register u_long _res __asm("d0");
+    register const struct ExecBase *_base __asm("a6") = SysBase;
+    register u_long d0 __asm("d0") = cacheBits;
+    register u_long d1 __asm("d1") = cacheMask;
+
+    __asm __volatile ("jsr a6@(-0x288)"
+		      : "=r" (_res)
+		      : "r" (_base), "r" (d0), "r" (d1)
+		      : "a0", "a1", "d0", "d1", "memory");
+    return(_res);
+}
+
+static __inline void Disable(void)
+{
+    register const struct ExecBase *_base __asm("a6") = SysBase;
+
+    __asm __volatile ("jsr a6@(-0x78)"
+		      : /* no output */
+		      : "r" (_base)
+		      : "a0", "a1", "d0", "d1", "memory");
+}
+
+static __inline void Enable(void)
+{
+    register const struct ExecBase *_base __asm("a6") = SysBase;
+
+    __asm __volatile ("jsr a6@(-0x7e)"
+		      : /* no output */
+		      : "r" (_base)
+		      : "a0", "a1", "d0", "d1", "memory");
+}
+
+static __inline struct Resident *FindResident(const u_char *name)
+{
+    register struct Resident *_res __asm("d0");
+    register const struct ExecBase *_base __asm("a6") = SysBase;
+    register const u_char *a1 __asm("a1") = name;
+
+    __asm __volatile ("jsr a6@(-0x60)"
+		      : "=r" (_res)
+		      : "r" (_base), "r" (a1)
+		      : "a0", "a1", "d0", "d1", "memory");
+    return _res;
+}
+
+static __inline void FreeMem(void *memoryBlock, u_long byteSize)
+{
+    register const struct ExecBase *_base __asm("a6") = SysBase;
+    register void *a1 __asm("a1") = memoryBlock;
+    register u_long d0 __asm("d0") = byteSize;
+
+    __asm __volatile ("jsr a6@(-0xd2)"
+		      : /* no output */
+		      : "r" (_base), "r" (a1), "r" (d0)
+		      : "a0", "a1", "d0", "d1", "memory");
+}
+
+static __inline void FreeVec(void *memoryBlock)
+{
+    register const struct ExecBase *_base __asm("a6") = SysBase;
+    register void *a1 __asm("a1") = memoryBlock;
+
+    __asm __volatile ("jsr a6@(-0x2b2)"
+		      : /* no output */
+		      : "r" (_base), "r" (a1)
+		      : "a0", "a1", "d0", "d1", "memory");
+}
+
+static __inline void *OpenResource(const u_char *resName)
+{
+    register void *_res  __asm("d0");
+    register const struct ExecBase *_base __asm("a6") = SysBase;
+    register const u_char *a1 __asm("a1") = resName;
+
+    __asm __volatile ("jsr a6@(-0x1f2)"
+		      : "=r" (_res)
+		      : "r" (_base), "r" (a1)
+		      : "a0", "a1", "d0", "d1", "memory");
+    return _res;
+}
+
+static __inline void *SuperState(void)
+{
+    register void *_res __asm("d0");
+    register const struct ExecBase *_base __asm("a6") = SysBase;
+
+    __asm __volatile ("jsr a6@(-0x96)"
+		      : "=r" (_res)
+		      : "r" (_base)
+		      : "a0", "a1", "d0", "d1", "memory");
+    return(_res);
+}
+
+static __inline u_long Supervisor(u_long (*userfunc)(void))
+{
+    register u_long _res __asm("d0");
+    register const struct ExecBase *_base __asm("a6") = SysBase;
+    register u_long (*d7)() __asm("d7") = userfunc;
+
+    __asm __volatile ("exg d7,a5;"
+		      "jsr a6@(-0x1e);"
+		      "exg d7,a5"
+		      : "=r" (_res)
+		      : "r" (_base), "r" (d7)
+		      : "a0", "a1", "d0", "d1", "memory");
+    return(_res);
+}
+
+
+extern const struct ExpansionBase *ExpansionBase;
+
+#define LVOFindConfigDev	(-0x48)
+
+static __inline struct ConfigDev *FindConfigDev(struct ConfigDev *oldConfigDev,
+						long manufacturer, long product)
+{
+    register struct ConfigDev *_res __asm("d0");
+    register const struct ExpansionBase *_base __asm("a6") = ExpansionBase;
+    register struct ConfigDev *a0 __asm("a0") = oldConfigDev;
+    register long d0 __asm("d0") = manufacturer;
+    register long d1 __asm("d1") = product;
+
+    __asm __volatile ("jsr a6@(-0x48)"
+		      : "=r" (_res)
+		      : "r" (_base), "r" (a0), "r" (d0), "r" (d1)
+		      : "a0", "a1", "d0", "d1", "memory");
+    return(_res);
+}
+
+
+extern const struct GfxBase *GfxBase;
+
+#define LVOLoadView		(-0xde)
+#define LVOSetChipRev		(-0x378)
+
+static __inline void LoadView(struct View *view)
+{
+    register const struct GfxBase *_base __asm("a6") = GfxBase;
+    register struct View *a1 __asm("a1") = view;
+
+    __asm __volatile ("jsr a6@(-0xde)"
+		      : /* no output */
+		      : "r" (_base), "r" (a1)
+		      : "a0", "a1", "d0", "d1", "memory");
+}
+
+static __inline u_long SetChipRev(u_long want)
+{
+    register u_long _res __asm("d0");
+    register const struct GfxBase *_base __asm("a6") = GfxBase;
+    register u_long d0 __asm("d0") = want;
+
+    __asm __volatile ("jsr a6@(-0x378)"
+		      : "=r" (_res)
+		      : "r" (_base), "r" (d0)
+		      : "a0", "a1", "d0", "d1", "memory");
+    return(_res);
+}
+
+
+    /*
+     *	Bootstrap Support Functions
+     */
+
+static __inline void disable_mmu(void)
+{
+    if (SysBase->AttnFlags & AFF_68040)
+	__asm __volatile ("moveq #0,d0;"
+			  ".long 0x4e7b0003;"	/* movec d0,tc */
+			  ".long 0x4e7b0004;"	/* movec d0,itt0 */
+			  ".long 0x4e7b0005;"	/* movec d0,itt1 */
+			  ".long 0x4e7b0006;"	/* movec d0,dtt0 */
+			  ".long 0x4e7b0007"	/* movec d0,dtt1 */
+			  : /* no outputs */
+			  : /* no inputs */
+			  : "d0");
+    else {
+	__asm __volatile ("subl #4,sp;"
+			  "pmove tc,sp@;"
+			  "bclr #7,sp@;"
+			  "pmove sp@,tc;"
+			  "addl #4,sp");
+	if (SysBase->AttnFlags & AFF_68030)
+	    __asm __volatile ("clrl sp@-;"
+			      ".long 0xf0170800;"	/* pmove sp@,tt0 */
+			      ".long 0xf0170c00;"	/* pmove sp@,tt1 */
+			      "addql #4,sp");
+    }
+}
diff -urN pc-2.0.21/arch/m68k/boot/atari/Makefile linux-2.0.21/arch/m68k/boot/atari/Makefile
--- pc-2.0.21/arch/m68k/boot/atari/Makefile	Wed Dec 27 21:44:40 1995
+++ linux-2.0.21/arch/m68k/boot/atari/Makefile	Thu Jan  1 01:00:00 1970
@@ -1,12 +0,0 @@
-
-CC		:= d:/gnu/bin/gcc.ttp
-CFLAGS	:= -Wall -O2 -fno-defer-pop -mint -s
-LD		:= d:/gnu/bin/gcc.ttp
-LDFLAGS	:= -mint -D__GNUC__
-
-bootstra.ttp: bootstra.o
-	$(LD) $(LDFLAGS) -o $@ $^
-	prgflags 7 7 $@
-	cp $@ d:/linux
-
-bootstra.o: bootstra.c bootinfo.h
diff -urN pc-2.0.21/arch/m68k/boot/atari/bootp.c linux-2.0.21/arch/m68k/boot/atari/bootp.c
--- pc-2.0.21/arch/m68k/boot/atari/bootp.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/arch/m68k/boot/atari/bootp.c	Sat Aug 24 14:29:20 1996
@@ -0,0 +1,793 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "bootp.h"
+
+
+/* --------------------------------------------------------------------- */
+/*						  Protocol Header Structures					 */
+
+struct etherhdr {
+	HWADDR			dst_addr;
+	HWADDR			src_addr;
+	unsigned short	type;
+};
+
+struct arphdr {
+	unsigned short	hrd;		/* format of hardware address	*/
+	unsigned short	pro;		/* format of protocol address	*/
+	unsigned char	hln;		/* length of hardware address	*/
+	unsigned char	pln;		/* length of protocol address	*/
+	unsigned short	op;			/* ARP opcode (command)			*/
+	unsigned char	addr[0];	/* addresses (var len)			*/
+};
+	
+struct iphdr {
+	unsigned char	version : 4;
+	unsigned char	ihl : 4;
+	unsigned char	tos;
+	unsigned short	tot_len;
+	unsigned short	id;
+	unsigned short	frag_off;
+	unsigned char	ttl;
+	unsigned char	protocol;
+	unsigned short	chksum;
+	IPADDR			src_addr;
+	IPADDR			dst_addr;
+};
+
+struct udphdr {
+	unsigned short	src_port;
+	unsigned short	dst_port;
+	unsigned short	len;
+	unsigned short	chksum;
+};
+
+struct bootp {
+	unsigned char	op;			/* packet opcode type */
+	unsigned char	htype;		/* hardware addr type */
+	unsigned char	hlen;		/* hardware addr length */
+	unsigned char	hops;		/* gateway hops */
+	unsigned long	xid;		/* transaction ID */
+	unsigned short	secs;		/* seconds since boot began */
+	unsigned short	unused;
+	IPADDR			ciaddr;		/* client IP address */
+	IPADDR			yiaddr;		/* 'your' IP address */
+	IPADDR			siaddr;		/* server IP address */
+	IPADDR			giaddr; 	/* gateway IP address */
+	unsigned char	chaddr[16];	/* client hardware address */
+	unsigned char	sname[64];	/* server host name */
+	unsigned char	file[128];	/* boot file name */
+	unsigned char	vend[64];	/* vendor-specific area */
+};
+
+struct tftp_req {
+	unsigned short	opcode;
+	char			name[512];
+};
+
+struct tftp_data {
+	unsigned short	opcode;
+	unsigned short	nr;
+	unsigned char	data[512];
+};
+
+struct tftp_ack {
+	unsigned short	opcode;
+	unsigned short	nr;
+};
+
+struct tftp_error {
+	unsigned short	opcode;
+	unsigned short	errcode;
+	char			str[512];
+};
+
+
+typedef struct {
+	struct etherhdr		ether;
+	struct arphdr		arp;
+} ARP;
+
+typedef struct {
+	struct etherhdr		ether;
+	struct iphdr		ip;
+	struct udphdr		udp;
+} UDP;
+
+#define	UDP_BOOTPS	67
+#define	UDP_BOOTPC	68
+#define	UDP_TFTP	69
+
+typedef struct {
+	struct etherhdr		ether;
+	struct iphdr		ip;
+	struct udphdr		udp;
+	struct bootp		bootp;
+} BOOTP;
+
+#define	BOOTREQUEST		1
+#define	BOOTREPLY		2
+#define	BOOTP_RETRYS	5
+
+typedef struct {
+	struct etherhdr		ether;
+	struct iphdr		ip;
+	struct udphdr		udp;
+	union tftp {
+		unsigned short		opcode;
+		struct tftp_req		req;
+		struct tftp_data	data;
+		struct tftp_ack		ack;
+		struct tftp_error	error;
+	} tftp;
+} TFTP;
+	
+#define	TFTP_RRQ	1
+#define	TFTP_WRQ	2
+#define	TFTP_DATA	3
+#define	TFTP_ACK	4
+#define	TFTP_ERROR	5
+
+
+/* --------------------------------------------------------------------- */
+/*								  Addresses								 */
+
+static HWADDR	MyHwaddr;
+static HWADDR	ServerHwaddr;
+static IPADDR	MyIPaddr;
+static IPADDR	ServerIPaddr;
+
+static IPADDR	IP_Unknown_Addr =   0x00000000;
+static IPADDR	IP_Broadcast_Addr = 0xffffffff;
+static HWADDR 	Eth_Broadcast_Addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+
+#define	HZ	200
+#define	_hz_200	(*(volatile unsigned long *)0x4ba)
+
+
+/* --------------------------------------------------------------------- */
+/*								Error Strings							 */
+
+static char *ErrStr[] = {
+	"timeout",
+	"general Ethernet transmit error",
+	"general Ethernet receive error",
+	"Ethernet framing error",
+	"Ethernet overflow error",
+	"Ethernet CRC error"
+};
+
+
+/* --------------------------------------------------------------------- */
+/*						 Kfile Emulation Definitions					 */
+
+#define	KFILE_CHUNK_BITS	16  /* chunk is 64 KB */
+#define	KFILE_CHUNK_SIZE	(1 << KFILE_CHUNK_BITS)
+#define	KFILE_CHUNK_MASK	(KFILE_CHUNK_SIZE-1)
+#define	KFILE_N_CHUNKS		(2*1024*1024/KFILE_CHUNK_SIZE)
+
+char *KFile[KFILE_N_CHUNKS];
+int  KFileSize = 0;
+int	 KFpos = 0;
+
+
+
+
+/***************************** Prototypes *****************************/
+
+static void free_kfile( void );
+static int bootp( char *image_name );
+static int tftp( char *image_name );
+static int udp_send( UDP *pkt, int len, int fromport, int toport );
+static unsigned short ip_checksum( struct iphdr *buf );
+static int udp_rcv( UDP *pkt, int *len, int fromport, int atport );
+static void print_ip( IPADDR addr );
+static void print_hw( HWADDR addr );
+static int check_ethif( void );
+static int eth_send( Packet *pkt, int len );
+static int eth_rcv( Packet *pkt, int *len );
+
+/************************* End of Prototypes **************************/
+
+
+
+
+/* --------------------------------------------------------------------- */
+/*						   Interface to bootstrap.c						 */
+
+/* get_remote_kernel():
+ * Perform all necessary steps to get the kernel image
+ * from the boot server. If successfull (retval == 0), subsequent calls to
+ * kread() can access the data.
+ */
+
+int get_remote_kernel( const char *kname /* optional */ )
+
+{	char	image_name[256];
+	
+	/* Check if a Ethernet interface is present and determine the Ethernet
+	 * address */
+	if (check_ethif() < 0) {
+		printf( "No Ethernet interface found -- no remote boot possible.\n" );
+		return( -1 );
+	}
+	
+	/* Do a BOOTP request to find out our IP address and the kernel image's
+	 * name; we also learn the IP and Ethernet address of our server */
+	if (kname)
+		strcpy( image_name, kname );
+	else
+		*image_name = 0;
+	if (bootp( image_name ) < 0)
+		return( -1 );
+	
+	/* Now start a TFTP connection to receive the kernel image */
+	if (tftp( image_name ) < 0)
+		return( -1 );
+
+	return( 0 );
+}
+
+
+/* kread(), klseek(), kclose():
+ * Functions for accessing the received kernel image like with read(),
+ * lseek(), close().
+ */
+
+int kread( int fd, void *buf, unsigned cnt )
+
+{	unsigned done = 0;
+	
+	if (!KFileSize)
+		return( read( fd, buf, cnt ) );
+
+	if (KFpos + cnt > KFileSize)
+		cnt = KFileSize - KFpos;
+	
+	while( cnt > 0 ) {
+		unsigned chunk = KFpos >> KFILE_CHUNK_BITS;
+		unsigned endchunk = (chunk+1) << KFILE_CHUNK_BITS;
+		unsigned n = cnt;
+
+		if (KFpos + n > endchunk)
+			n = endchunk - KFpos;
+		memcpy( buf, KFile[chunk] + (KFpos & KFILE_CHUNK_MASK), n );
+		cnt -= n;
+		buf += n;
+		done += n;
+		KFpos += n;
+	}
+
+	return( done );
+}
+
+
+int klseek( int fd, int where, int whence )
+
+{
+	if (!KFileSize)
+		return( lseek( fd, where, whence ) );
+
+	switch( whence ) {
+	  case SEEK_SET:
+		KFpos = where;
+		break;
+	  case SEEK_CUR:
+		KFpos += where;
+		break;
+	  case SEEK_END:
+		KFpos = KFileSize + where;
+		break;
+	  default:
+		return( -1 );
+	}
+	if (KFpos < 0) {
+		KFpos = 0;
+		return( -1 );
+	}
+	else if (KFpos > KFileSize) {
+		KFpos = KFileSize;
+		return( -1 );
+	}
+
+	return( KFpos );
+}
+
+
+int kclose( int fd )
+
+{
+	if (!KFileSize)
+		return( close( fd ) );
+
+	free_kfile();
+	return( 0 );
+}
+
+
+static void free_kfile( void )
+
+{	int		i;
+
+	for( i = 0; i < KFILE_N_CHUNKS; ++i )
+		if (KFile[i]) free( KFile[i] );
+}
+
+
+
+/* --------------------------------------------------------------------- */
+/*							   BOOTP Procedure							 */
+
+
+static int bootp( char *image_name )
+
+{	BOOTP	req;
+	Packet	_reply;
+	BOOTP	*reply = (BOOTP *)_reply;
+	static unsigned char mincookie[] = { 99, 130, 83, 99, 255 };
+	unsigned long 	starttime, rancopy;
+	int				err, len, retry;
+	
+	memset( (char *)&req, 0, sizeof(req) );
+	/* Now fill in the packet... */
+	req.bootp.op = BOOTREQUEST;
+	req.bootp.htype = 1; /* 10Mb/s Ethernet */
+	req.bootp.hlen = 6;
+	memcpy( req.bootp.chaddr, &MyHwaddr, ETHADDRLEN );
+
+	/* Put in the minimal RFC1497 Magic cookie */
+	memcpy( req.bootp.vend, mincookie, sizeof(mincookie) );
+	/* Put the user precified bootfile name in place */
+	memcpy( req.bootp.file, image_name, strlen(image_name)+1);
+
+	starttime = _hz_200;
+	for( retry = 0; retry < BOOTP_RETRYS; ++retry ) {
+
+		/* Initialize server addresses and own IP to defaults */
+		ServerIPaddr = IP_Broadcast_Addr;  /* 255.255.255.255 */
+		MyIPaddr     = IP_Unknown_Addr;    /* 0.0.0.0 */
+		memcpy( ServerHwaddr, Eth_Broadcast_Addr, ETHADDRLEN );
+
+		if (retry)
+			sleep( 3 );
+		
+		req.bootp.xid = rancopy = _hz_200;
+		req.bootp.secs = (_hz_200 - starttime) / HZ;
+
+		if ((err = udp_send( (UDP *)&req, sizeof(req.bootp),
+							 UDP_BOOTPC, UDP_BOOTPS )) < 0) {
+			printf( "bootp send: %s\n", ErrStr[-err-1] );
+			continue;
+		}
+		
+		if ((err = udp_rcv( (UDP *)reply, &len,
+							UDP_BOOTPS, UDP_BOOTPC )) < 0) {
+			printf( "bootp rcv: %s\n", ErrStr[-err-1] );
+			continue;
+		}
+		if (len < sizeof(struct bootp)) {
+			printf( "received short BOOTP packet (%d bytes)\n", len );
+			continue;
+		}
+
+		if (reply->bootp.xid == rancopy)
+			/* Ok, got the answer */
+			break;
+		printf( "bootp: xid mismatch\n" );
+	}
+	if (retry >= BOOTP_RETRYS) {
+		printf( "No response from a bootp server\n" );
+		return( -1 );
+	}
+	
+	ServerIPaddr = reply->bootp.siaddr;
+	memcpy( ServerHwaddr, reply->ether.src_addr, ETHADDRLEN );
+	printf( "\nBoot server is " );
+	if (strlen(reply->bootp.sname) > 0)
+		printf( "%s, IP ", reply->bootp.sname );
+	print_ip( ServerIPaddr );
+	printf( ", HW address " );
+	print_hw( ServerHwaddr );
+	printf( "\n" );
+
+	MyIPaddr = reply->bootp.yiaddr;
+	printf( "My IP address is " );
+	print_ip( MyIPaddr );
+	printf( "\n" );
+
+	strcpy( image_name, reply->bootp.file );
+	return( 0 );
+}
+
+
+/* --------------------------------------------------------------------- */
+/*								TFTP Procedure							 */
+
+
+static int tftp( char *image_name )
+
+{	TFTP	spkt;
+	Packet	_rpkt;
+	TFTP	*rpkt = (TFTP *)&_rpkt;
+	unsigned short	mytid, rtid = 0;
+	int				blk, retries, i, wpos, err, len, datalen;
+	static char		rotchar[4] = { '|', '/', '-', '\\' };
+	
+	retries = 5;
+	/* Construct and send a read request */
+  repeat_req:
+	spkt.tftp.req.opcode = TFTP_RRQ;
+	strcpy( spkt.tftp.req.name, image_name );
+	strcpy( spkt.tftp.req.name + strlen(spkt.tftp.req.name) + 1, "octet" );
+	mytid = _hz_200 & 0xffff;
+	
+	if ((err = udp_send( (UDP *)&spkt, sizeof(spkt.tftp.req.opcode) +
+									   strlen(image_name) + 1 +
+									   strlen( "octect" ) +1,
+						 mytid, UDP_TFTP )) < 0) {
+		printf( "TFTP RREQ: %s\n", ErrStr[-err-1] );
+		if (--retries > 0)
+			goto repeat_req;
+		return( -1 );
+	}
+
+	retries = 5;
+	for( i = 0; i < KFILE_N_CHUNKS; ++i )
+		KFile[i] = NULL;
+	wpos = 0;
+	printf( "Receiving kernel image %s:\n", image_name );
+
+	for( blk = 1; ; ++blk ) {
+
+	  repeat_data:
+		if ((err = udp_rcv( (UDP *)rpkt, &len, rtid, mytid )) < 0) {
+			printf( "TFTP rcv: %s\n", ErrStr[-err-1] );
+			if (--retries > 0)
+				goto repeat_data;
+			goto err;
+		}
+		if (rtid == 0)
+			/* Store the remote port at the first packet received */
+			rtid = rpkt->udp.src_port;
+
+		if (rpkt->tftp.opcode == TFTP_ERROR) {
+			if (strlen(rpkt->tftp.error.str) > 0)
+				printf( "TFTP error: %s\n", rpkt->tftp.error.str );
+			else
+				printf( "TFTP error #%d (no description)\n",
+						rpkt->tftp.error.errcode );
+			goto err;
+		}
+		else if (rpkt->tftp.opcode != TFTP_DATA) {
+			printf( "Bad TFTP packet type: %d\n", rpkt->tftp.opcode );
+			if (--retries > 0)
+				goto repeat_data;
+			goto err;
+		}
+
+		if (rpkt->tftp.data.nr != blk) {
+			/* doubled data packet; ignore it */
+			goto repeat_data;
+		}
+		datalen = len - sizeof(rpkt->tftp.data.opcode) -
+			            sizeof(rpkt->tftp.data.nr);
+		
+		/* store data */
+		if (datalen > 0) {
+			int chunk = wpos >> KFILE_CHUNK_BITS;
+			if (chunk >= KFILE_N_CHUNKS) {
+				printf( "TFTP: file too large! Aborting.\n" );
+			  out_of_mem:
+				spkt.tftp.error.opcode = TFTP_ERROR;
+				spkt.tftp.error.errcode = 3;
+				strcpy( spkt.tftp.error.str, "Out of memory" );
+				udp_send( (UDP *)&spkt, sizeof(spkt.tftp.ack), mytid, rtid );
+				goto err;
+			}
+			if (!KFile[chunk]) {
+				if (!(KFile[chunk] = malloc( KFILE_CHUNK_SIZE ))) {
+					printf( "TFTP: Out of memory for kernel image\n" );
+					goto out_of_mem;
+				}
+			}
+			memcpy( KFile[chunk] + (wpos & KFILE_CHUNK_MASK),
+					rpkt->tftp.data.data, datalen );
+			wpos += datalen;
+
+#define	DISPLAY_BITS 13
+			if ((wpos & ((1 << DISPLAY_BITS)-1)) == 0) {
+				printf( "\r %c %7d Bytes ",
+						rotchar[(wpos>>DISPLAY_BITS)&3], wpos );
+				fflush( stdout );
+			}
+		}
+
+		/* Send ACK packet */
+	  repeat_ack:
+		spkt.tftp.ack.opcode = TFTP_ACK;
+		spkt.tftp.ack.nr = blk;
+		if ((err = udp_send( (UDP *)&spkt, sizeof(spkt.tftp.ack),
+							 mytid, rtid )) < 0) {
+			printf( "TFTP ACK: %s\n", ErrStr[-err-1] );
+			if (--retries > 0)
+				goto repeat_ack;
+			goto err;
+		}
+
+		if (datalen < 512) {
+			/* This was the last packet */
+			printf( "\r   %7d Bytes done\n\n", wpos );
+			break;
+		}
+
+		retries = 5;
+	}
+
+	KFileSize = wpos;
+	return( 0 );
+
+  err:
+	free_kfile();
+	return( -1 );
+}
+
+
+
+/* --------------------------------------------------------------------- */
+/*				  UDP/IP Protocol Quick Hack Implementation				 */
+
+
+static int udp_send( UDP *pkt, int len, int fromport, int toport )
+
+{
+	/* UDP layer */
+	pkt->udp.src_port = fromport;
+	pkt->udp.dst_port = toport;
+	pkt->udp.len      = (len += sizeof(struct udphdr));
+	pkt->udp.chksum   = 0; /* Too lazy to calculate :-) */
+
+	/* IP layer */
+	pkt->ip.version  = 4;
+	pkt->ip.ihl      = 5;
+	pkt->ip.tos      = 0;
+	pkt->ip.tot_len  = (len += sizeof(struct iphdr));
+	pkt->ip.id       = 0;
+	pkt->ip.frag_off = 0;
+	pkt->ip.ttl      = 255;
+	pkt->ip.protocol = 17; /* UDP */
+	pkt->ip.src_addr = MyIPaddr;
+	pkt->ip.dst_addr = ServerIPaddr;
+	pkt->ip.chksum   = 0;
+	pkt->ip.chksum   = ip_checksum( &pkt->ip );
+
+	/* Ethernet layer */
+	memcpy( &pkt->ether.dst_addr, ServerHwaddr, ETHADDRLEN );
+	memcpy( &pkt->ether.src_addr, MyHwaddr, ETHADDRLEN );
+	pkt->ether.type     = 0x0800;
+	len += sizeof(struct etherhdr);
+
+	return( eth_send( (Packet *)pkt, len ) );
+}
+
+
+static unsigned short ip_checksum( struct iphdr *buf )
+
+{	unsigned long sum = 0, wlen = 5;
+
+	__asm__ ("subqw #1,%2\n"
+			 "1:\t"
+			 "movel %1@+,%/d0\n\t"
+			 "addxl %/d0,%0\n\t"
+			 "dbra %2,1b\n\t"
+			 "movel %0,%/d0\n\t"
+			 "swap %/d0\n\t"
+			 "addxw %/d0,%0\n\t"
+			 "clrw %/d0\n\t"
+			 "addxw %/d0,%0"
+			 : "=d" (sum), "=a" (buf), "=d" (wlen)
+			 : "0" (sum), "1" (buf), "2" (wlen)
+			 : "d0");
+	return( (~sum) & 0xffff );
+}
+
+
+static int udp_rcv( UDP *pkt, int *len, int fromport, int atport )
+
+{	int				err;
+
+  repeat:
+	if ((err = eth_rcv( (Packet *)pkt, len )))
+		return( err );
+	
+	/* Ethernet layer */
+	if (pkt->ether.type == 0x0806) {
+		/* ARP */
+		ARP *pk = (ARP *)pkt;
+		unsigned char *shw, *sip, *thw, *tip;
+
+		if (pk->arp.hrd != 1 || pk->arp.pro != 0x0800 ||
+			pk->arp.op != 1 || MyIPaddr == IP_Unknown_Addr)
+			/* Wrong hardware type or protocol; or reply -> ignore */
+			goto repeat;
+		shw = pk->arp.addr;
+		sip = shw + pk->arp.hln;
+		thw = sip + pk->arp.pln;
+		tip = thw + pk->arp.hln;
+		
+		if (memcmp( tip, &MyIPaddr, pk->arp.pln ) == 0) {
+			memcpy( thw, shw, pk->arp.hln );
+			memcpy( tip, sip, pk->arp.pln );
+			memcpy( shw, &MyHwaddr, pk->arp.hln );
+			memcpy( sip, &MyIPaddr, pk->arp.pln );
+
+			memcpy( &pk->ether.dst_addr, thw, ETHADDRLEN );
+			memcpy( &pk->ether.src_addr, &MyHwaddr, ETHADDRLEN );
+			eth_send( (Packet *)pk, *len );
+		}
+		goto repeat;
+	}
+	else if (pkt->ether.type != 0x0800) {
+		printf( "Unknown Ethernet packet type %04x received\n",
+				pkt->ether.type );
+		goto repeat;
+	}
+
+	/* IP layer */
+	if (MyIPaddr != IP_Unknown_Addr && pkt->ip.dst_addr != MyIPaddr) {
+		printf( "Received packet for wrong IP address\n" );
+		goto repeat;
+	}
+	if (ServerIPaddr != IP_Unknown_Addr &&
+		ServerIPaddr != IP_Broadcast_Addr &&
+		pkt->ip.src_addr != ServerIPaddr) {
+		printf( "Received packet from wrong server\n" );
+		goto repeat;
+	}
+	/* If IP header is longer than 5 longs, delete the options */
+	if (pkt->ip.ihl > 5) {
+		char *udpstart = (char *)((long *)&pkt->ip + pkt->ip.ihl);
+		memmove( &pkt->udp, udpstart, *len - (udpstart-(char *)pkt) );
+	}
+	
+	/* UDP layer */
+	if (fromport != 0 && pkt->udp.src_port != fromport) {
+		printf( "Received packet from wrong port %d\n", pkt->udp.src_port );
+		goto repeat;
+	}
+	if (pkt->udp.dst_port != atport) {
+		printf( "Received packet at wrong port %d\n", pkt->udp.dst_port );
+		goto repeat;
+	}
+
+	*len = pkt->udp.len - sizeof(struct udphdr);
+	return( 0 );
+}
+
+
+/* --------------------------------------------------------------------- */
+/*							   Address Printing							 */
+
+
+static void print_ip( IPADDR addr )
+
+{
+	printf( "%ld.%ld.%ld.%ld",
+			(addr >> 24) & 0xff,
+			(addr >> 16) & 0xff,
+			(addr >>  8) & 0xff,
+			addr & 0xff );
+}
+
+
+static void print_hw( HWADDR addr )
+
+{
+	printf( "%02x:%02x:%02x:%02x:%02x:%02x",
+			addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] );
+}
+
+
+/* --------------------------------------------------------------------- */
+/*					 Ethernet Interface Abstraction Layer				 */
+
+
+#ifdef ETHLL_LANCE
+#include "ethlance.h"
+#endif
+
+static ETHIF_SWITCH *PossibleInterfaces[] = {
+#ifdef ETHLL_LANCE
+	&LanceSwitch,
+#endif
+};
+
+#define	N_PossibleInterfaces (sizeof(PossibleInterfaces)/sizeof(*PossibleInterfaces))
+
+/* Detected interface */
+static ETHIF_SWITCH *Ethif = NULL;
+
+
+static int check_ethif( void )
+
+{	int i;
+
+	/* Check for configured interfaces */
+	Ethif = NULL;
+	for( i = 0; i < N_PossibleInterfaces; ++i ) {
+		if (PossibleInterfaces[i]->probe() >= 0) {
+			Ethif = PossibleInterfaces[i];
+			break;
+		}
+	}
+	if (!Ethif)
+		return( -1 );
+
+	if (Ethif->init() < 0) {
+		printf( "Ethernet interface initialization failed\n" );
+		return( -1 );
+	}
+	Ethif->get_hwaddr( &MyHwaddr );
+	return( 0 );
+}
+
+
+static int eth_send( Packet *pkt, int len )
+
+{
+	return( Ethif->snd( pkt, len ));
+}
+
+
+static int eth_rcv( Packet *pkt, int *len )
+
+{
+	return( Ethif->rcv( pkt, len ));
+}
+
+
+#if 0
+static void dump_packet( UDP *pkt )
+
+{	int		i, l;
+	unsigned char *p;
+	
+	printf( "Packet dump:\n" );
+
+	printf( "Ethernet header:\n" );
+	printf( "  dst addr: " ); print_hw( pkt->ether.dst_addr ); printf( "\n" );
+	printf( "  src addr: " ); print_hw( pkt->ether.src_addr ); printf( "\n" );
+	printf( "  type: %04x\n", pkt->ether.type );
+
+	printf( "IP header:\n" );
+	printf( "  version: %d\n", pkt->ip.version );
+	printf( "  hdr len: %d\n", pkt->ip.ihl );
+	printf( "  tos: %d\n", pkt->ip.tos );
+	printf( "  tot_len: %d\n", pkt->ip.tot_len );
+	printf( "  id: %d\n", pkt->ip.id );
+	printf( "  frag_off: %d\n", pkt->ip.frag_off );
+	printf( "  ttl: %d\n", pkt->ip.ttl );
+	printf( "  prot: %d\n", pkt->ip.protocol );
+	printf( "  src addr: " ); print_ip( pkt->ip.src_addr ); printf( "\n" );
+	printf( "  dst addr: " ); print_ip( pkt->ip.dst_addr ); printf( "\n" );
+
+	printf( "UDP header:\n" );
+	printf( "  src port: %d\n", pkt->udp.src_port );
+	printf( "  dst port: %d\n", pkt->udp.dst_port );
+	printf( "  len: %d\n", pkt->udp.len );
+
+	printf( "Data:" );
+	l = pkt->udp.len - sizeof(pkt->udp);
+	p = (unsigned char *)&pkt->udp + sizeof(pkt->udp);
+	for( i = 0; i < l; ++i ) {
+		if ((i % 32) == 0)
+			printf( "\n  %04x ", i );
+		printf( "%02x ", *p );
+	}
+	printf( "\n" );
+}
+#endif
diff -urN pc-2.0.21/arch/m68k/boot/atari/bootp.h linux-2.0.21/arch/m68k/boot/atari/bootp.h
--- pc-2.0.21/arch/m68k/boot/atari/bootp.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/arch/m68k/boot/atari/bootp.h	Sat Aug 24 14:29:21 1996
@@ -0,0 +1,44 @@
+#ifndef _bootp_h
+#define _bootp_h
+
+/* --------------------------------------------------------------------- */
+/*							 Ethernet Definitions						 */
+
+#define	PKTLEN			1544
+typedef unsigned char	Packet[PKTLEN];
+
+#define	ETHADDRLEN		6
+typedef unsigned char	HWADDR[ETHADDRLEN];
+
+typedef struct {
+	int		(*probe)( void );
+	int		(*init)( void );
+	void	(*get_hwaddr)( HWADDR *addr );
+	int		(*snd)( Packet *pkt, int len );
+	int		(*rcv)( Packet *pkt, int *len );
+} ETHIF_SWITCH;
+
+
+/* error codes */
+#define	ETIMEO	-1		/* Timeout */
+#define	ESEND	-2		/* General send error (carrier, abort, ...) */
+#define	ERCV	-3		/* General receive error */
+#define	EFRAM	-4		/* Framing error */
+#define	EOVERFL	-5		/* Overflow (too long packet) */
+#define	ECRC	-6		/* CRC error */
+
+
+typedef unsigned long IPADDR;
+
+
+/***************************** Prototypes *****************************/
+
+int get_remote_kernel( const char *kname );
+int kread( int fd, void *buf, unsigned cnt );
+int klseek( int fd, int where, int whence );
+int kclose( int fd );
+
+/************************* End of Prototypes **************************/
+
+#endif  /* _bootp_h */
+
diff -urN pc-2.0.21/arch/m68k/boot/atari/bootstrap.c linux-2.0.21/arch/m68k/boot/atari/bootstrap.c
--- pc-2.0.21/arch/m68k/boot/atari/bootstrap.c	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/boot/atari/bootstrap.c	Sat Sep 14 15:17:34 1996
@@ -43,7 +43,7 @@
 #include <asm/page.h>
 
 #define _LINUX_TYPES_H		/* Hack to prevent including <linux/types.h> */
-#include <asm/bootinfo.h>
+#include <asm/setup.h>
 
 /* Atari bootstrap include file */
 #include "bootstrap.h"
@@ -162,6 +162,33 @@
 }
 
 
+/* Test if FPU instructions are executed in hardware, or if they're
+   emulated in software.  For this, the F-line vector is temporarily
+   replaced. */
+
+int test_software_fpu(void)
+{
+	int rv = 0;
+
+	__asm__ __volatile__
+		( "movel	0x2c,a0\n\t"
+		  "movel	sp,a1\n\t"
+		  "movel	#Lfline,0x2c\n\t"
+		  "moveq	#1,%0\n\t"
+		  "fnop 	\n\t"
+		  "nop		\n\t"
+		  "moveq	#0,%0\n"
+		"Lfline:\t"
+		  "movel	a1,sp\n\t"
+		  "movel	a0,0x2c"
+		  : "=d" (rv)
+		  : /* no inputs */
+		  : "a0", "a1" );
+
+	return rv;
+}
+
+
 void get_medusa_bank_sizes( u_long *bank1, u_long *bank2 )
 
 {	static u_long	save_addr;
@@ -248,6 +275,47 @@
 #undef TESTADDR
 #undef TESTPAT
 
+
+static int check_bootinfo_version(char *memptr)
+{
+    struct bootversion *bv = (struct bootversion *)memptr;
+    unsigned long version = 0;
+    int i, kernel_major, kernel_minor, boots_major, boots_minor;
+
+    printf( "\n" );
+    if (bv->magic == BOOTINFOV_MAGIC) {
+	for( i = 0; bv->machversions[i].machtype != 0; ++i ) {
+	    if (bv->machversions[i].machtype == MACH_ATARI) {
+		version = bv->machversions[i].version;
+		break;
+	    }
+	}
+    }
+    if (!version)
+	printf("Kernel has no bootinfo version info, assuming 0.0\n");
+
+    kernel_major = BI_VERSION_MAJOR(version);
+    kernel_minor = BI_VERSION_MINOR(version);
+    boots_major  = BI_VERSION_MAJOR(ATARI_BOOTI_VERSION);
+    boots_minor  = BI_VERSION_MINOR(ATARI_BOOTI_VERSION);
+    printf("Bootstrap's bootinfo version: %d.%d\n",
+	   boots_major, boots_minor);
+    printf("Kernel's bootinfo version   : %d.%d\n",
+	   kernel_major, kernel_minor);
+    
+    if (kernel_major != boots_major) {
+	printf("\nThis bootstrap is too %s for this kernel!\n",
+	       boots_major < kernel_major ? "old" : "new");
+	return 0;
+    }
+    if (kernel_minor > boots_minor) {
+	printf("Warning: Bootinfo version of bootstrap and kernel differ!\n");
+	printf("         Certain features may not work.\n");
+    }
+    return 1;
+}
+
+
 #ifdef USE_BOOTP
 # include "bootp.h"
 #else
@@ -287,7 +355,7 @@
     kernel_name = "vmlinux";
 
     /* print the startup message */
-    puts("\fLinux/68k Atari Bootstrap version 1.6"
+    puts("\fLinux/68k Atari Bootstrap version 1.8"
 #ifdef USE_BOOTP
 	 " (with BOOTP)"
 #endif
@@ -329,7 +397,7 @@
 	    break;
 #ifdef USE_BOOTP
 	  case 'b':
-	    prefer_bootp = 1;
+	    prefer_bootp = 0;
 	    break;
 #endif
 	  case '?':
@@ -370,8 +438,7 @@
     getcookie("_MCH", &mch_type);
 
     /* check if we are on a 68030/40 with FPU */
-    if ((cpu_type != 30 && cpu_type != 40 && cpu_type != 60) ||
-		(fpu_type >> 16) < 2)
+    if ((cpu_type != 30 && cpu_type != 40 && cpu_type != 60))
     {
 	puts("Machine type currently not supported. Aborting...");
 	boot_exit(EXIT_FAILURE);
@@ -404,10 +471,13 @@
 		puts( "68060\n" );
 	}
 	else {
-		switch ((fpu_type >> 16) & 6) {
+		switch ((fpu_type >> 16) & 7) {
 		  case 0:
 			puts("not present\n");
 			break;
+		  case 1:
+			puts("SFP004 not supported. Assuming no FPU.");
+			break;
 		  case 2:
 			/* try to determine real type */
 			if (fpu_idle_frame_size () != 0x18)
@@ -427,6 +497,14 @@
 			break;
 		}
 	}
+	/* ++roman: If an FPU was announced in the cookie, test
+	   whether it is a real hardware FPU or a software emulator!  */
+	if (bi.cputype & FPU_MASK) {
+		if (test_software_fpu()) {
+			bi.cputype &= ~FPU_MASK;
+			puts("FPU: software emulated. Assuming no FPU.");
+		}
+	}
 
     memset(&bi.bi_atari.hw_present, 0, sizeof(bi.bi_atari.hw_present));
 
@@ -766,7 +844,9 @@
       }
     else
       kernel_size = kexec.a_text + kexec.a_data + kexec.a_bss;
-    memreq = kernel_size + sizeof (bi) + rd_size;
+    memreq = kernel_size + sizeof (bi);
+    /* align load address of ramdisk image, read() is sloooow on odd addr. */
+    memreq = ((memreq + 3) & ~3) + rd_size;
 	
     /* allocate RAM for the kernel */
     if (!(memptr = (char *)Malloc (memreq)))
@@ -824,6 +904,12 @@
       }
     kclose (kfd);
 
+    /* Check kernel's bootinfo version */
+    if (!check_bootinfo_version(memptr)) {
+	Mfree ((void *)memptr);
+	boot_exit (EXIT_FAILURE);
+    }
+    
     /* copy the boot_info struct to the end of the kernel image */
     memcpy ((void *)(memptr + kernel_size),
 	    &bi, sizeof(bi));
@@ -837,7 +923,7 @@
 	    Mfree ((void *)memptr);
 	    boot_exit (EXIT_FAILURE);
 	}
-	if (read (rfd, memptr + kernel_size + sizeof (bi),
+	if (read (rfd, memptr + memreq - rd_size,
 		  rd_size) != rd_size)
 	{
 	    fprintf (stderr, "Failed to read ramdisk file\n");
@@ -852,7 +938,7 @@
     {
 	if (bi.ramdisk_size)
 	    printf ("RAM disk at %#lx, size is %ldK\n",
-		    (u_long)memptr + kernel_size,
+		    (u_long)(memptr + memreq - rd_size),
 		    bi.ramdisk_size);
 
 	if (elf_kernel)
@@ -879,10 +965,8 @@
 		elf_kernel ? kexec_elf.e_entry : kexec.a_entry);
 	printf ("ramdisk dest top is %#lx\n", bi.ramdisk_addr + rd_size);
 	printf ("ramdisk lower limit is %#lx\n",
-		(u_long)(memptr + kernel_size));
-	printf ("ramdisk src top is %#lx\n",
-		(u_long)(memptr + kernel_size) +
-		rd_size);
+		(u_long)(memptr + memreq - rd_size));
+	printf ("ramdisk src top is %#lx\n", (u_long)(memptr + memreq));
 
 	printf ("Type a key to continue the Linux boot...");
 	fflush (stdout);
diff -urN pc-2.0.21/arch/m68k/boot/atari/ethlance.c linux-2.0.21/arch/m68k/boot/atari/ethlance.c
--- pc-2.0.21/arch/m68k/boot/atari/ethlance.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/arch/m68k/boot/atari/ethlance.c	Sat Aug 24 14:29:22 1996
@@ -0,0 +1,435 @@
+
+#include <stdio.h>
+#include <string.h>
+
+#include "bootp.h"
+#include "ethlance.h"
+
+
+struct {
+	volatile unsigned short	*memaddr;
+	volatile unsigned short	*ioaddr;
+} lance_addr_list[] = {
+	{ (void *)0xfe010000, (void *)0xfe00fff0 },	/* RieblCard VME in TT */
+	{ (void *)0xfec10000, (void *)0xfec0fff0 },	/* RieblCard VME in MegaSTE
+												   (highest byte stripped) */
+	{ (void *)0xfee00000, (void *)0xfeff7000 },	/* RieblCard in ST
+												   (highest byte stripped) */
+	{ (void *)0xfecf0000, (void *)0xfecffff0 },	/* PAMCard VME in TT and MSTE
+												   (highest byte stripped) */
+};
+
+#define	N_LANCE_ADDR	(sizeof(lance_addr_list)/sizeof(*lance_addr_list))
+
+#define TX_RING_SIZE			1
+#define TX_RING_LEN_BITS		0
+
+#define RX_RING_SIZE			16
+#define RX_RING_LEN_BITS		(4 << 5)
+
+#define	offsetof(type,elt)	((unsigned long)(&(((type *)0)->elt)))
+
+/* The LANCE Rx and Tx ring descriptors. */
+struct lance_rx_head {
+	unsigned short			base;		/* Low word of base addr */
+	volatile unsigned char	flag;
+	unsigned char			base_hi;	/* High word of base addr (unused) */
+	short					buf_length;	/* This length is 2s complement! */
+	short					msg_length;	/* This length is "normal". */
+};
+
+struct lance_tx_head {
+	unsigned short			base;		/* Low word of base addr */
+	volatile unsigned char	flag;
+	unsigned char			base_hi;	/* High word of base addr (unused) */
+	short					length;		/* Length is 2s complement! */
+	volatile short			misc;
+};
+
+struct ringdesc {
+	unsigned short	adr_lo;		/* Low 16 bits of address */
+	unsigned char	len;		/* Length bits */
+	unsigned char	adr_hi;		/* High 8 bits of address (unused) */
+};
+
+struct lance_packet {
+	volatile unsigned char	data[PKTLEN];
+};
+
+/* The LANCE initialization block, described in databook. */
+struct lance_init_block {
+	unsigned short	mode;		/* Pre-set mode */
+	unsigned char	hwaddr[6];	/* Physical ethernet address */
+	unsigned		filter[2];	/* Multicast filter (unused). */
+	/* Receive and transmit ring base, along with length bits. */
+	struct ringdesc	rx_ring;
+	struct ringdesc	tx_ring;
+};
+
+/* The whole layout of the Lance shared memory */
+struct lance_memory {
+	struct lance_init_block	init;
+	struct lance_tx_head	tx_head[TX_RING_SIZE];
+	struct lance_rx_head	rx_head[RX_RING_SIZE];
+	struct lance_packet		tx_packet[TX_RING_SIZE];
+	struct lance_packet		rx_packet[TX_RING_SIZE];
+};
+
+#define RIEBL_MAGIC			0x09051990
+#define RIEBL_MAGIC_ADDR	((unsigned long *)(((char *)MEM) + 0xee8a))
+#define RIEBL_HWADDR_ADDR	((unsigned char *)(((char *)MEM) + 0xee8e))
+#define RIEBL_IVEC_ADDR		((unsigned short *)(((char *)MEM) + 0xfffe))
+
+struct lance_ioreg {
+/* base+0x0 */	volatile unsigned short	data;
+/* base+0x2 */	volatile unsigned short	addr;
+				unsigned char			_dummy1[3];
+/* base+0x7 */	volatile unsigned char	ivec;
+				unsigned char			_dummy2[5];
+/* base+0xd */	volatile unsigned char	eeprom;
+				unsigned char			_dummy3;
+/* base+0xf */	volatile unsigned char	mem;
+};
+
+enum lance_type {
+	OLD_RIEBL,		/* old Riebl card without battery */
+	NEW_RIEBL,		/* new Riebl card with battery */
+	PAM_CARD		/* PAM card with EEPROM */
+} CardType;
+
+HWADDR	dev_addr;
+
+/* This is a default address for the old RieblCards without a battery
+ * that have no ethernet address at boot time. 00:00:36:04 is the
+ * prefix for Riebl cards, the 00:00 at the end is arbitrary.
+ */
+
+HWADDR OldRieblDefHwaddr = {
+	0x00, 0x00, 0x36, 0x04, 0x00, 0x00
+};
+
+struct lance_ioreg	*IO;
+struct lance_memory	*MEM;
+
+#define	DREG	IO->data
+#define	AREG	IO->addr
+#define	REGA(a)	( AREG = (a), DREG )
+
+int CurRx;
+
+
+/* Definitions for the Lance */
+
+/* tx_head flags */
+#define	TMD1_ENP		0x01
+#define TMD1_STP		0x02
+#define	TMD1_DEF		0x04
+#define TMD1_ONE		0x08
+#define	TMD1_MORE		0x10
+#define	TMD1_ERR		0x40
+#define TMD1_OWN 		0x80
+
+#define TMD1_OWN_CHIP	TMD1_OWN
+#define TMD1_OWN_HOST	0
+
+/* tx_head misc field */
+#define TMD3_TDR		0x03FF
+#define TMD3_RTRY		0x0400
+#define TMD3_LCAR		0x0800
+#define TMD3_LCOL		0x1000
+#define TMD3_UFLO		0x4000
+#define TMD3_BUFF3		0x8000
+
+/* rx_head flags */
+#define	RMD1_ENP		0x01
+#define RMD1_STP		0x02
+#define RMD1_BUFF		0x04
+#define RMD1_CRC		0x08
+#define RMD1_OFLO		0x10
+#define RMD1_FRAM		0x20
+#define	RMD1_ERR		0x40
+#define RMD1_OWN 		0x80
+
+#define RMD1_OWN_CHIP	RMD1_OWN
+#define RMD1_OWN_HOST	0
+
+/* register names */
+#define CSR0	0
+#define CSR1	1
+#define CSR2	2
+#define CSR3	3
+
+/* CSR0 */
+#define CSR0_INIT	0x0001		/* initialize */
+#define CSR0_STRT	0x0002		/* start */
+#define CSR0_STOP	0x0004		/* stop */
+#define CSR0_TDMD	0x0008		/* transmit demand */
+#define CSR0_TXON	0x0010		/* transmitter on */
+#define CSR0_RXON	0x0020		/* receiver on */
+#define CSR0_INEA	0x0040		/* interrupt enable */
+#define CSR0_INTR	0x0080		/* interrupt active */
+#define CSR0_IDON	0x0100		/* initialization done */
+#define CSR0_TINT	0x0200		/* transmitter interrupt */
+#define CSR0_RINT	0x0400		/* receiver interrupt */
+#define CSR0_MERR	0x0800		/* memory error */
+#define CSR0_MISS	0x1000		/* missed frame */
+#define CSR0_CERR	0x2000		/* carrier error (no heartbeat :-) */
+#define CSR0_BABL	0x4000		/* babble: tx-ed too many bits */
+#define CSR0_ERR	0x8000		/* error */
+
+/* CSR3 */
+#define CSR3_BCON	0x0001
+#define CSR3_ACON	0x0002
+#define CSR3_BSWP	0x0004
+
+
+#define	HZ	200
+#define	_hz_200	(*(volatile unsigned long *)0x4ba)
+
+
+
+
+/***************************** Prototypes *****************************/
+
+static int lance_probe( void );
+static int addr_readable( volatile void *regp, int wordflag );
+static int lance_init( void );
+static void lance_get_hwaddr( HWADDR *addr );
+static int lance_snd( Packet *pkt, int len );
+static int lance_rcv( Packet *pkt, int *len );
+
+/************************* End of Prototypes **************************/
+
+
+
+ETHIF_SWITCH LanceSwitch = {
+	lance_probe, lance_init, lance_get_hwaddr, 
+	lance_snd, lance_rcv
+};
+
+
+static int lance_probe( void )
+
+{	int		i;
+	
+	for( i = 0; i < N_LANCE_ADDR; ++i ) {
+		if (addr_readable( lance_addr_list[i].memaddr, 1 ) &&
+			(lance_addr_list[i].memaddr[0] = 1,
+			 lance_addr_list[i].memaddr[0] == 1) &&
+			(lance_addr_list[i].memaddr[0] = 0,
+			 lance_addr_list[i].memaddr[0] == 0) &&
+			addr_readable( lance_addr_list[i].ioaddr, 1 )) {
+			break;
+		}
+	}
+	if (i == N_LANCE_ADDR) return( -1 );
+
+	IO = (struct lance_ioreg *)lance_addr_list[i].ioaddr;
+	MEM = (struct lance_memory *)lance_addr_list[i].memaddr;
+	REGA( CSR0 ) = CSR0_STOP;
+
+	return( 0 );
+}
+
+
+static int addr_readable( volatile void *regp, int wordflag )
+
+{	int		ret;
+	long	*vbr, save_berr;
+
+	__asm__ __volatile__ ( "movec	%/vbr,%0" : "=r" (vbr) : );
+	save_berr = vbr[2];
+	
+	__asm__ __volatile__
+	(	"movel	%/sp,%/d1\n\t"
+		"movel	#Lberr,%2@\n\t"
+		"moveq	#0,%0\n\t"
+		"tstl   %3\n\t"
+		"bne	1f\n\t"
+		"tstb	%1@\n\t"
+		"bra	2f\n"
+"1:		 tstw	%1@\n"
+"2:		 moveq	#1,%0\n"
+"Lberr:	 movel	%/d1,%/sp"
+		: "=&d" (ret)
+		: "a" (regp), "a" (&vbr[2]), "rm" (wordflag)
+		: "d1", "memory"
+	);
+
+	vbr[2] = save_berr;
+	
+	return( ret );
+}
+
+
+static int lance_init( void )
+
+{	int		i;
+	
+	/* Now test for type: If the eeprom I/O port is readable, it is a
+	 * PAM card */
+	if (addr_readable( &(IO->eeprom), 0 )) {
+		/* Switch back to Ram */
+		i = IO->mem;
+		CardType = PAM_CARD;
+	}
+	else if (*RIEBL_MAGIC_ADDR == RIEBL_MAGIC) {
+		CardType = NEW_RIEBL;
+	}
+	else
+		CardType = OLD_RIEBL;
+		
+	/* Get the ethernet address */
+	switch( CardType ) {
+	  case OLD_RIEBL:
+		/* No ethernet address! (Set some default address) */
+		memcpy( dev_addr, OldRieblDefHwaddr, ETHADDRLEN );
+		break;
+	  case NEW_RIEBL:
+		memcpy( dev_addr, RIEBL_HWADDR_ADDR, ETHADDRLEN );
+		break;
+	  case PAM_CARD:
+		i = IO->eeprom;
+		for( i = 0; i < ETHADDRLEN; ++i )
+			dev_addr[i] = 
+				((((unsigned short *)MEM)[i*2] & 0x0f) << 4) |
+				((((unsigned short *)MEM)[i*2+1] & 0x0f));
+		i = IO->mem;
+		break;
+	}
+
+	MEM->init.mode = 0x0000;		/* Disable Rx and Tx. */
+	for( i = 0; i < ETHADDRLEN; i++ )
+		MEM->init.hwaddr[i] = dev_addr[i^1]; /* <- 16 bit swap! */
+	MEM->init.filter[0] = 0x00000000;
+	MEM->init.filter[1] = 0x00000000;
+	MEM->init.rx_ring.adr_lo = offsetof( struct lance_memory, rx_head );
+	MEM->init.rx_ring.adr_hi = 0;
+	MEM->init.rx_ring.len    = RX_RING_LEN_BITS;
+	MEM->init.tx_ring.adr_lo = offsetof( struct lance_memory, tx_head );
+	MEM->init.tx_ring.adr_hi = 0;
+	MEM->init.tx_ring.len    = TX_RING_LEN_BITS;
+	
+	REGA( CSR3 ) = CSR3_BSWP | (CardType == PAM_CARD ? CSR3_ACON : 0); 
+	REGA( CSR2 ) = 0;
+	REGA( CSR1 ) = 0;
+	REGA( CSR0 ) = CSR0_INIT | CSR0_STRT;
+
+	i = 1000000;
+	while( i-- > 0 )
+		if (DREG & CSR0_IDON)
+			break;
+	if (i < 0 || (DREG & CSR0_ERR)) {
+		DREG = CSR0_STOP;
+		return( -1 );
+	}
+	DREG = CSR0_IDON;
+
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		MEM->tx_head[i].base = offsetof( struct lance_memory, tx_packet[i] );
+		MEM->tx_head[i].flag = TMD1_OWN_HOST;
+ 		MEM->tx_head[i].base_hi = 0;
+		MEM->tx_head[i].length = 0;
+		MEM->tx_head[i].misc = 0;
+	}
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		MEM->rx_head[i].base = offsetof( struct lance_memory, rx_packet[i] );
+		MEM->rx_head[i].flag = TMD1_OWN_CHIP;
+		MEM->rx_head[i].base_hi = 0;
+		MEM->rx_head[i].buf_length = -PKTLEN;
+		MEM->rx_head[i].msg_length = 0;
+	}
+	CurRx = 0;
+	
+	return( 0 );
+}
+
+
+static void lance_get_hwaddr( HWADDR *addr )
+
+{
+	memcpy( addr, dev_addr, ETHADDRLEN );
+}
+
+
+static int lance_snd( Packet *pkt, int len )
+
+{	unsigned long timeout;
+	
+	/* The old LANCE chips doesn't automatically pad buffers to min. size. */
+	len = (len < 60) ? 60 : len;
+	/* PAM-Card has a bug: Can only send packets with even number of bytes! */
+	if (CardType == PAM_CARD && (len & 1))
+		++len;
+
+	MEM->tx_head[0].length = -len;
+	MEM->tx_head[0].misc = 0;
+	memcpy( (void *)&MEM->tx_packet[0].data, pkt, len );
+	MEM->tx_head[0].base = offsetof(struct lance_memory, tx_packet[0]);
+	MEM->tx_head[0].base_hi = 0;
+	MEM->tx_head[0].flag = TMD1_OWN_CHIP | TMD1_ENP | TMD1_STP;
+
+	/* Trigger an immediate send poll. */
+	REGA( CSR0 ) = CSR0_TDMD;
+
+	/* Wait for packet being sent */
+	timeout = _hz_200 + 3*HZ;
+	while( (MEM->tx_head[0].flag & TMD1_OWN_CHIP) &&
+		   !MEM->tx_head[0].misc &&
+		   _hz_200 < timeout )
+		;
+
+	if ((MEM->tx_head[0].flag & TMD1_OWN) == TMD1_OWN_HOST &&
+		!(MEM->tx_head[0].misc & TMD1_ERR))
+		/* sent ok */
+		return( 0 );
+
+	/* failure */
+	if (_hz_200 >= timeout)
+		return( ETIMEO );
+	if (MEM->tx_head[0].misc & TMD3_UFLO) {
+		/* On FIFO errors, must re-turn on TX! */
+		DREG = CSR0_STRT;
+	}
+
+	return( ESEND );
+}
+
+
+static int lance_rcv( Packet *pkt, int *len )
+
+{	unsigned long	timeout;
+	int				stat;
+	
+	/* Wait for a packet */
+	timeout = _hz_200 + 4*HZ;
+	while( (MEM->rx_head[CurRx].flag & TMD1_OWN_CHIP) &&
+		   _hz_200 < timeout )
+		;
+	/* Not ours -> was a timeout */
+	if (((stat = MEM->rx_head[CurRx].flag) & TMD1_OWN) == TMD1_OWN_CHIP)
+		return( ETIMEO );
+
+	/* Check for errors */
+	if (stat != (RMD1_ENP|RMD1_STP)) {
+		MEM->rx_head[CurRx].flag &= (RMD1_ENP|RMD1_STP);
+		if (stat & RMD1_FRAM) return( EFRAM );
+		if (stat & RMD1_OFLO) return( EOVERFL );
+		if (stat & RMD1_CRC)  return( ECRC );
+		return( ERCV );
+	}
+
+	/* Get the packet */
+	*len = MEM->rx_head[CurRx].msg_length & 0xfff;
+	memcpy( pkt, (void *)&MEM->rx_packet[CurRx].data, *len );
+
+	/* Give the buffer back to the chip */
+	MEM->rx_head[CurRx].buf_length = -PKTLEN;
+	MEM->rx_head[CurRx].flag |= RMD1_OWN_CHIP;
+	CurRx = (CurRx + 1) % RX_RING_SIZE;
+
+	return( 0 );
+}
+
+
diff -urN pc-2.0.21/arch/m68k/boot/atari/ethlance.h linux-2.0.21/arch/m68k/boot/atari/ethlance.h
--- pc-2.0.21/arch/m68k/boot/atari/ethlance.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/arch/m68k/boot/atari/ethlance.h	Sat Aug 24 14:29:23 1996
@@ -0,0 +1,7 @@
+
+#ifndef _ethlance_h
+#define _ethlance_h
+
+extern ETHIF_SWITCH LanceSwitch;
+
+#endif /* _ethlance_h */
diff -urN pc-2.0.21/arch/m68k/config.in linux-2.0.21/arch/m68k/config.in
--- pc-2.0.21/arch/m68k/config.in	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/config.in	Thu Sep 26 20:03:34 1996
@@ -5,6 +5,11 @@
 mainmenu_name "Linux/68k Kernel Configuration"
 
 mainmenu_option next_comment
+comment 'Code maturity level options'
+bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
+endmenu
+
+mainmenu_option next_comment
 comment 'Loadable module support'
 bool 'Enable loadable module support' CONFIG_MODULES
 if [ "$CONFIG_MODULES" = "y" ]; then
@@ -14,22 +19,48 @@
 endmenu
 
 mainmenu_option next_comment
-comment 'General setup'
+comment 'Platform dependant setup'
 
 bool 'Amiga support' CONFIG_AMIGA
 bool 'Atari support' CONFIG_ATARI
 bool 'Macintosh support' CONFIG_MAC
-bool '68040 floating point software package' CONFIG_FPSP_040
-bool '68060 integer/floating point software package' CONFIG_IFPSP_060
+
+comment 'Processor type'
+bool '68020 support' CONFIG_M68020
+bool '68030 support' CONFIG_M68030
+bool '68040 support' CONFIG_M68040
+if [ "$CONFIG_M68040" = "y" ]; then
+  bool 'Use 68040 specific optimizations' CONFIG_OPTIMIZE_040
+fi
+bool '68060 support' CONFIG_M68060
+if [ "$CONFIG_M68060" = "y" ]; then
+  bool 'Use 68060 specific optimizations' CONFIG_OPTIMIZE_060
+fi
+bool 'Advanced processor options' CONFIG_ADVANCED_CPU
+if [ "$CONFIG_ADVANCED_CPU" = "y" ]; then
+  bool 'Use read-modify-write instructions' CONFIG_RMW_INSNS
+fi
+endmenu
+
+mainmenu_option next_comment
+comment 'General setup'
+
 bool 'Networking support' CONFIG_NET
 bool 'System V IPC' CONFIG_SYSVIPC
 tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
-if [ "$CONFIG_BINFMT_ELF" = "y" ]; then
-  bool 'Compile kernel as ELF - if your GCC is ELF-GCC' CONFIG_KERNEL_ELF
-fi
+bool 'Compile kernel as ELF - if your GCC is ELF-GCC' CONFIG_KERNEL_ELF
 if [ "$CONFIG_AMIGA" = "y" ]; then
   bool 'Amiga AutoConfig Identification' CONFIG_ZORRO
+  bool 'Amiga OCS chipset support' CONFIG_AMIFB_OCS
+  bool 'Amiga ECS chipset support' CONFIG_AMIFB_ECS
+  bool 'Amiga AGA chipset support' CONFIG_AMIFB_AGA
+  bool 'Amiga Cybervision support' CONFIG_FB_CYBER
+#  bool 'Amiga GSP (TMS340x0) support' CONFIG_AMIGA_GSP
+#  if [ "$CONFIG_AMIGA_GSP" = "y" ]; then
+#    bool 'DMI Resolver support' CONFIG_GSP_RESOLVER
+#    bool 'A2410 support' CONFIG_GSP_A2410
+#  fi
 fi
 endmenu
 
@@ -41,20 +72,33 @@
 
 tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD
 bool 'IDE harddisk support' CONFIG_BLK_DEV_IDE
+if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
+  bool '   Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD
+fi
 if [ "$CONFIG_AMIGA" = "y" ]; then
-bool 'Amiga Cybervision support' CONFIG_FB_CYBER
-bool 'Amiga Zorro II ramdisk support' CONFIG_AMIGA_Z2RAM
+tristate 'Amiga Zorro II ramdisk support' CONFIG_AMIGA_Z2RAM
 fi
 if [ "$CONFIG_ATARI" = "y" ]; then
 bool 'Atari ACSI support' CONFIG_ATARI_ACSI
+if [ "$CONFIG_ATARI_ACSI" = "y" ]; then
+comment 'Some devices (e.g. CD jukebox) support multiple LUNs'
+bool 'Probe all LUNs on each ACSI device' CONFIG_ACSI_MULTI_LUN
+bool 'Atari SLM laser printer support' CONFIG_ATARI_SLM
+fi
 fi
 
+comment 'Additional Block Devices'
+
+tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
+bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
+if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
+  tristate '   Linear (append) mode' CONFIG_MD_LINEAR
+  tristate '   RAID-0 (striping) mode' CONFIG_MD_STRIPED
+fi
 tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
 if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
   bool '   Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD
 fi
-
-tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
 endmenu
 
 if [ "$CONFIG_NET" = "y" ]; then
@@ -68,11 +112,11 @@
 
 if [ "$CONFIG_SCSI" != "n" ]; then
 
-comment 'SCSI support type (disk, tape, CDrom)'
+comment 'SCSI support type (disk, tape, CD-ROM)'
 
 dep_tristate 'SCSI disk support' CONFIG_BLK_DEV_SD $CONFIG_SCSI
 dep_tristate 'SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI
-dep_tristate 'SCSI CDROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI
+dep_tristate 'SCSI CD-ROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI
 dep_tristate 'SCSI generic support' CONFIG_CHR_DEV_SG $CONFIG_SCSI
 
 comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs'
@@ -88,9 +132,11 @@
 bool 'A3000 WD33C93A support' CONFIG_A3000_SCSI
 bool 'A2091 WD33C93A support' CONFIG_A2091_SCSI
 bool 'GVP Series II WD33C93A support' CONFIG_GVP11_SCSI
+bool 'CyberStorm SCSI support' CONFIG_CYBERSTORM_SCSI
 fi
 if [ "$CONFIG_ATARI" = "y" ]; then
 dep_tristate 'Atari native SCSI support' CONFIG_ATARI_SCSI $CONFIG_SCSI
+bool 'Long delays for Toshiba CD-ROMs' CONFIG_ATARI_SCSI_TOSHIBA_DELAY
 fi
 #dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI
 endmenu
@@ -112,6 +158,8 @@
 tristate 'SLIP (serial line) support' CONFIG_SLIP
 if [ "$CONFIG_SLIP" != "n" ]; then
   bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED
+  bool ' Keepalive and linefill' CONFIG_SLIP_SMART
+  bool ' Six bit SLIP encapsulation' CONFIG_SLIP_MODE_SLIP6
 fi
 tristate 'PPP (point-to-point) support' CONFIG_PPP
 if [ ! "$CONFIG_PPP" = "n" ]; then
@@ -125,6 +173,10 @@
 fi
 if [ "$CONFIG_ATARI" = "y" ]; then
   bool 'Atari Lance support' CONFIG_ATARILANCE
+if [ "$CONFIG_ATARI_ACSI" = "y" ]; then
+  bool 'BioNet-100 support' CONFIG_ATARI_BIONET
+  bool 'PAMsNet support' CONFIG_ATARI_PAMSNET
+fi
 fi
 fi
 endmenu
@@ -136,19 +188,20 @@
 mainmenu_option next_comment
 comment 'Character devices'
 
-bool 'Parallel printer support' CONFIG_PRINTER
+tristate 'Parallel printer support' CONFIG_PRINTER
 if [ "$CONFIG_AMIGA" = "y" ]; then
+  dep_tristate 'Multiface Card III parallel support' CONFIG_MULTIFACE_III_LP $CONFIG_PRINTER
   tristate 'Amiga mouse support' CONFIG_AMIGAMOUSE
 fi
 if [ "$CONFIG_ATARI" = "y" ]; then
   tristate 'Atari mouse support' CONFIG_ATARIMOUSE
 fi
-if [ "$CONFIG_ATARI" = y ]; then
+if [ "$CONFIG_ATARI" = "y" ]; then
   tristate 'Atari MFP serial support' CONFIG_ATARI_MFPSER
   tristate 'Atari SCC serial support' CONFIG_ATARI_SCC
   tristate 'Atari MIDI serial support' CONFIG_ATARI_MIDI
 fi
-if [ "$CONFIG_AMIGA" = y ]; then
+if [ "$CONFIG_AMIGA" = "y" ]; then
   tristate 'Amiga builtin serial support' CONFIG_AMIGA_BUILTIN_SERIAL
   bool 'GVP IO-Extender support' CONFIG_GVPIOEXT
   tristate 'Multiface Card III serial support' CONFIG_MULTIFACE_III_TTY
diff -urN pc-2.0.21/arch/m68k/console/Makefile linux-2.0.21/arch/m68k/console/Makefile
--- pc-2.0.21/arch/m68k/console/Makefile	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/console/Makefile	Sun Sep  8 00:47:28 1996
@@ -6,10 +6,19 @@
 # unless it's something special (ie not a .c file).
 #
 
-EXTRA_CFLAGS := -Wa,-m68030
+GSPA = gspa
+GSPH2C = gspahextoc
 
 L_TARGET = console.a
 L_OBJS = fbcon.o fonts.o font_8x16.o font_8x8.o pearl_8x8.o
 M_OBJS =
 
+ifdef CONFIG_AMIGA_GSP
+L_OBJS := $(L_OBJS) gspcon.o gspcore.o
+endif
+
 include $(TOPDIR)/Rules.make
+
+gspcore.c: gspcore.gsp
+	$(GSPA) $< > $*.hex
+	$(GSPH2C) $*.hex > gspcore.c
diff -urN pc-2.0.21/arch/m68k/console/fbcon.c linux-2.0.21/arch/m68k/console/fbcon.c
--- pc-2.0.21/arch/m68k/console/fbcon.c	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/console/fbcon.c	Wed Sep 18 00:21:58 1996
@@ -40,6 +40,7 @@
 
 
 #include <linux/types.h>
+#include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/tty.h>
@@ -49,7 +50,7 @@
 #include <linux/kd.h>
 #include <linux/malloc.h>
 
-#include <asm/bootinfo.h>
+#include <asm/setup.h>
 #include <asm/irq.h>
 #ifdef CONFIG_AMIGA
 #include <asm/amigahw.h>
@@ -269,7 +270,7 @@
 static __inline__ u_long expand2l(u_char c);
 static __inline__ u_short dup2w(u_char c);
 static __inline__ int real_y(struct display *p, int y);
-static void fbcon_vbl_handler(int irq, struct pt_regs *fp, void *dummy);
+static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp);
 static void fbcon_bmove_rec(struct display *p, int sy, int sx, int dy, int dx,
                             int height, int width, u_int y_break);
 
@@ -520,19 +521,19 @@
 #ifdef CONFIG_AMIGA
    if (MACH_IS_AMIGA) {
       cursor_blink_rate = AMIGA_CURSOR_BLINK_RATE;
-      irqres = add_isr(IRQ_AMIGA_VERTB, fbcon_vbl_handler, 0, NULL,
-                       "console/cursor");
+      irqres = request_irq(IRQ_AMIGA_VERTB, fbcon_vbl_handler, 0,
+                           "console/cursor", fbcon_vbl_handler);
    }
 #endif /* CONFIG_AMIGA */
 #ifdef CONFIG_ATARI
    if (MACH_IS_ATARI) {
       cursor_blink_rate = ATARI_CURSOR_BLINK_RATE;
-      irqres = add_isr(IRQ_AUTO_4, fbcon_vbl_handler, IRQ_TYPE_PRIO, NULL,
-		       "console/cursor");
+      irqres = request_irq(IRQ_AUTO_4, fbcon_vbl_handler, IRQ_TYPE_PRIO,
+                           "console/cursor", fbcon_vbl_handler);
    }
 #endif /* CONFIG_ATARI */
 
-   if (!irqres)
+   if (irqres)
       panic("fbcon_startup: Couldn't add vblank interrupt");
 
    return(kmem_start);
@@ -1487,7 +1488,7 @@
 }
 
 
-static void fbcon_vbl_handler(int irq, struct pt_regs *fp, void *dummy)
+static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp)
 {
    struct display *p;
 
@@ -1713,7 +1714,7 @@
 {
 	int unit = conp->vc_num;
 	struct display *p = &disp[unit];
-	int i, size, alloc;
+	int i, j, size, alloc;
 
 	size = (p->fontwidth+7)/8 * p->fontheight * 256;
 	alloc = (*w+7)/8 * *h * 256;
@@ -1724,10 +1725,9 @@
 		/* allocation length not sufficient */
 		return( -ENAMETOOLONG );
 
-	if ((i = verify_area( VERIFY_WRITE, (void *)data, size )))
-		return i;
-
-	memcpy_tofs( data, p->fontdata, size );
+	for (i = 0; i < 256; i++)
+		for (j = 0; j < p->fontheight; j++)
+			data[i*32+j] = p->fontdata[i*p->fontheight+j];
 	return( 0 );
 }
 
@@ -1738,7 +1738,7 @@
 {
 	int unit = conp->vc_num;
 	struct display *p = &disp[unit];
-	int i, size, userspace = 1, resize;
+	int i, j, size, userspace = 1, resize;
 	char *old_data = NULL, *new_data;
 
 	if (w < 0)
@@ -1794,13 +1794,15 @@
 		old_data = p->fontdata;
 	
 	if (userspace) {
-		if ((i = verify_area( VERIFY_READ, (void *)data, size )))
-			return i;
 		if (!(new_data = kmalloc( sizeof(int)+size, GFP_USER )))
 			return( -ENOMEM );
 		new_data += sizeof(int);
 		REFCOUNT(new_data) = 1; /* usage counter */
-		memcpy_fromfs( new_data, data, size );
+
+		for (i = 0; i < 256; i++)
+			for (j = 0; j < h; j++)
+				new_data[i*h+j] = data[i*32+j];
+
 		p->fontdata = new_data;
 		p->userfont = 1;
 	}
@@ -1814,6 +1816,9 @@
   activate:
 	if (resize) {
 		p->var.xoffset = p->var.yoffset = p->yscroll = 0;  /* reset wrap/pan */
+		/* Adjust the virtual screen-size to fontheight*rows */
+		p->var.yres_virtual = (p->var.yres/h)*h;
+		p->vrows = p->var.yres_virtual/h;
 		if (divides(p->ywrapstep, p->fontheight))
 			p->scrollmode = SCROLL_YWRAP;
 		else if (divides(p->ypanstep, p->fontheight) &&
diff -urN pc-2.0.21/arch/m68k/console/fonts.c linux-2.0.21/arch/m68k/console/fonts.c
--- pc-2.0.21/arch/m68k/console/fonts.c	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/console/fonts.c	Sat Aug 24 14:29:31 1996
@@ -12,8 +12,8 @@
 
 #include <linux/types.h>
 #include <linux/string.h>
+#include <asm/setup.h>
 #include <asm/font.h>
-#include <asm/bootinfo.h>
 
 
    /*
diff -urN pc-2.0.21/arch/m68k/defconfig linux-2.0.21/arch/m68k/defconfig
--- pc-2.0.21/arch/m68k/defconfig	Mon May  6 11:44:30 1996
+++ linux-2.0.21/arch/m68k/defconfig	Thu Sep 26 21:21:44 1996
@@ -3,6 +3,11 @@
 #
 
 #
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
 # Loadable module support
 #
 # CONFIG_MODULES is not set
@@ -10,30 +15,58 @@
 # CONFIG_KERNELD is not set
 
 #
-# General setup
+# Platform dependant setup
 #
 CONFIG_AMIGA=y
 # CONFIG_ATARI is not set
 # CONFIG_MAC is not set
-CONFIG_FPSP_040=y
-# CONFIG_IFPSP_060 is not set
+
+#
+# Processor type
+#
+CONFIG_M68020=y
+CONFIG_M68030=y
+CONFIG_M68040=y
+# CONFIG_OPTIMIZE_040 is not set
+# CONFIG_M68060 is not set
+# CONFIG_OPTIMIZE_060 is not set
+# CONFIG_ADVANCED_CPU is not set
+# CONFIG_RMW_INSNS is not set
+
+#
+# General setup
+#
 CONFIG_NET=y
 CONFIG_SYSVIPC=y
 CONFIG_BINFMT_AOUT=y
 CONFIG_BINFMT_ELF=y
 CONFIG_KERNEL_ELF=y
-# CONFIG_ZORRO is not set
+CONFIG_ZORRO=y
+CONFIG_AMIFB_OCS=y
+CONFIG_AMIFB_ECS=y
+CONFIG_AMIFB_AGA=y
+# CONFIG_FB_CYBER is not set
+# CONFIG_AMIGA_GSP is not set
+# CONFIG_GSP_RESOLVER is not set
+# CONFIG_GSP_A2410 is not set
 
 #
 # Block device driver configuration
 #
 CONFIG_BLK_DEV_FD=y
-CONFIG_BLK_DEV_RAM=y
 # CONFIG_BLK_DEV_LOOP is not set
 # CONFIG_BLK_DEV_IDE is not set
-# CONFIG_FB_CYBER is not set
+# CONFIG_BLK_DEV_IDECD is not set
 # CONFIG_AMIGA_Z2RAM is not set
 # CONFIG_ATARI_ACSI is not set
+# CONFIG_ACSI_MULTI_LUN is not set
+# CONFIG_ATARI_SLM
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_STRIPED is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_INITRD=y
 
 #
 # Networking options
@@ -89,6 +122,7 @@
 CONFIG_A3000_SCSI=y
 # CONFIG_A2091_SCSI is not set
 # CONFIG_GVP11_SCSI is not set
+# CONFIG_CYBERSTORM_SCSI is not set
 # CONFIG_ATARI_SCSI is not set
 
 #
@@ -102,11 +136,14 @@
 # CONFIG_A2065 is not set
 # CONFIG_HYDRA is not set
 # CONFIG_ATARILANCE is not set
+# CONFIG_ATARI_BIONET is not set
+# CONFIG_ATARI_PAMSNET is not set
 
 #
 # Filesystems
 #
 # CONFIG_QUOTA is not set
+# CONFIG_LOCK_MANDATORY is not set
 CONFIG_MINIX_FS=y
 # CONFIG_EXT_FS is not set
 CONFIG_EXT2_FS=y
@@ -115,6 +152,7 @@
 CONFIG_MSDOS_FS=y
 # CONFIG_VFAT_FS is not set
 # CONFIG_UMSDOS_FS is not set
+# CONFIG_MSDOS_PARTITION is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
 # CONFIG_ROOT_NFS is not set
@@ -131,6 +169,11 @@
 # CONFIG_PRINTER is not set
 CONFIG_AMIGAMOUSE=y
 # CONFIG_ATARIMOUSE is not set
+CONFIG_AMIGA_BUILTIN_SERIAL=y
+# CONFIG_GVPIOEXT is not set
+# CONFIG_MULTIFACE_III_TTY is not set
+# CONFIG_USERIAL is not set
+# CONFIG_WATCHDOG is not set
 # CONFIG_UMISC is not set
 
 #
diff -urN pc-2.0.21/arch/m68k/fpsp040/skeleton.S linux-2.0.21/arch/m68k/fpsp040/skeleton.S
--- pc-2.0.21/arch/m68k/fpsp040/skeleton.S	Mon May  6 11:44:30 1996
+++ linux-2.0.21/arch/m68k/fpsp040/skeleton.S	Sat Aug 24 14:30:02 1996
@@ -46,15 +46,6 @@
 |
 |	The following counters are used for standalone testing
 |
-sigunimp:	.long	0
-sigbsun:		.long	0
-siginex:		.long	0
-sigdz:		.long	0
-sigunfl:		.long	0
-sigovfl:		.long	0
-sigoperr:	.long	0
-sigsnan:		.long	0
-sigunsupp:	.long	0
 
 	|section 8
  
@@ -85,8 +76,6 @@
 	frestore	(%sp)+
 	unlk		%a6
 
-	addl	#1,sigdz		|for standalone testing
-
 	SAVE_ALL
 	moveq	#-1,%d0
 	movel	%d0,%sp@(LOFF_ORIG_D0)    | a -1 in the ORIG_D0 field
@@ -160,8 +149,6 @@
 
 real_inex:
 
-	addl		#1,siginex		|for standalone testing
-
 	link		%a6,#-LOCAL_SIZE
 	fsave		-(%sp)
 not_fmt40:
@@ -203,8 +190,6 @@
 	jmp	fpsp_ovfl
 real_ovfl:
 
-	addl		#1,sigovfl		|for standalone testing
-
 	link		%a6,#-LOCAL_SIZE
 	fsave		-(%sp)
 	bclrb		#E3,E_BYTE(%a6)		|clear and test E3 flag
@@ -234,8 +219,6 @@
 	jmp	fpsp_unfl
 real_unfl:
 
-	addl		#1,sigunfl		|for standalone testing
-
 	link		%a6,#-LOCAL_SIZE
 	fsave		-(%sp)
 	bclrb		#E3,E_BYTE(%a6)		|clear and test E3 flag
@@ -270,8 +253,6 @@
 	frestore	(%sp)+
 	unlk		%a6
 
-	addl		#1,sigsnan		|for standalone testing
-
 	SAVE_ALL
 	moveq	#-1,%d0
 	movel	%d0,%sp@(LOFF_ORIG_D0)    | a -1 in the ORIG_D0 field
@@ -297,9 +278,6 @@
 	frestore	(%sp)+
 	unlk		%a6
 
-	addl		#1,sigoperr		|for standalone testing
-
-
 	SAVE_ALL
 	moveq	#-1,%d0
 	movel	%d0,%sp@(LOFF_ORIG_D0)    | a -1 in the ORIG_D0 field
@@ -331,10 +309,6 @@
 	frestore	(%sp)+
 	unlk		%a6
 
-	addl		#1,sigbsun		|for standalone testing
-
-
-
 	SAVE_ALL
 	moveq	#-1,%d0
 	movel	%d0,%sp@(LOFF_ORIG_D0)    | a -1 in the ORIG_D0 field
@@ -359,9 +333,6 @@
 	jmp	fpsp_fline
 real_fline:
 
-	addl		#1,sigunimp		|for standalone testing
-
-
 	SAVE_ALL
 	moveq	#-1,%d0
 	movel	%d0,%sp@(LOFF_ORIG_D0)    | a -1 in the ORIG_D0 field
@@ -386,9 +357,6 @@
 	bclrb		#E1,E_BYTE(%a6)	|unsupp is always an E1 exception
 	frestore	(%sp)+
 	unlk		%a6
-
-	addl		#1,sigunsupp		|for standalone testing
-
 
 	SAVE_ALL
 	moveq	#-1,%d0
diff -urN pc-2.0.21/arch/m68k/ifpsp060/Makefile linux-2.0.21/arch/m68k/ifpsp060/Makefile
--- pc-2.0.21/arch/m68k/ifpsp060/Makefile	Tue Apr 23 16:14:22 1996
+++ linux-2.0.21/arch/m68k/ifpsp060/Makefile	Sat Aug 24 14:30:29 1996
@@ -4,15 +4,9 @@
 # License.  See the file "README.legal" in the main directory of this archive
 # for more details.
 
-#.c.s:
-#	$(CC) $(CFLAGS) -S $<
-#.c.o:
-#	$(CC) $(CFLAGS) -c $<
-#.s.o:
-#	$(AS) -o $*.o $<
 .S.o:
-	$(AS) -o $*.o $<
-#	$(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $<
+#	$(AS) -o $*.o $<
+	$(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $<
 
 OS_TARGET := ifpsp.o
 
diff -urN pc-2.0.21/arch/m68k/ifpsp060/fskeleton.S linux-2.0.21/arch/m68k/ifpsp060/fskeleton.S
--- pc-2.0.21/arch/m68k/ifpsp060/fskeleton.S	Tue Apr 23 16:14:22 1996
+++ linux-2.0.21/arch/m68k/ifpsp060/fskeleton.S	Mon Aug 26 23:55:00 1996
@@ -34,6 +34,7 @@
 |	(3) example "Call-out" table
 |
 
+#include <linux/linkage.h>
 
 |################################
 | (1) EXAMPLE CALL-OUTS		#
@@ -61,7 +62,7 @@
 |
 	.global		_060_fpsp_done
 _060_fpsp_done:
-	rte
+	bral	 _060_isp_done	| do the same as isp_done
 
 |
 | _060_real_ovfl():
@@ -79,7 +80,8 @@
 	fsave		-(%sp)
 	move.w		#0x6000,0x2(%sp)
 	frestore	(%sp)+
-	rte
+	bral		SYMBOL_NAME(trap)	| jump to trap handler
+
 
 |
 | _060_real_unfl():
@@ -97,7 +99,7 @@
 	fsave		-(%sp)
 	move.w		#0x6000,0x2(%sp)
 	frestore	(%sp)+
-	rte
+	bral		SYMBOL_NAME(trap)	| jump to trap handler
 
 |
 | _060_real_operr():
@@ -116,7 +118,7 @@
 	fsave		-(%sp)
 	move.w		#0x6000,0x2(%sp)
 	frestore	(%sp)+
-	rte
+	bral		SYMBOL_NAME(trap)	| jump to trap handler
 
 |
 | _060_real_snan():
@@ -135,7 +137,7 @@
 	fsave		-(%sp)
 	move.w		#0x6000,0x2(%sp)
 	frestore	(%sp)+
-	rte
+	bral		SYMBOL_NAME(trap)	| jump to trap handler
 
 |
 | _060_real_dz():
@@ -154,7 +156,7 @@
 	fsave		-(%sp)
 	move.w		#0x6000,0x2(%sp)
 	frestore	(%sp)+
-	rte
+	bral		SYMBOL_NAME(trap)	| jump to trap handler
 
 |
 | _060_real_inex():
@@ -173,7 +175,7 @@
 	fsave		-(%sp)
 	move.w		#0x6000,0x2(%sp)
 	frestore	(%sp)+
-	rte
+	bral		SYMBOL_NAME(trap)	| jump to trap handler
 
 |
 | _060_real_bsun():
@@ -189,14 +191,13 @@
 |
 	.global		_060_real_bsun
 _060_real_bsun:
-	fsave		-(%sp)
+|	fsave		-(%sp)
 
 	fmove.l		%fpsr,-(%sp)
 	andi.b		#0xfe,(%sp)
 	fmove.l		(%sp)+,%fpsr
 
-	add.l		#0xc,%sp
-	rte
+	bral		SYMBOL_NAME(trap)	| jump to trap handler
 
 |
 | _060_real_fline():
@@ -210,7 +211,7 @@
 | 
 	.global		_060_real_fline
 _060_real_fline:
-	bras		_060_real_fline
+	bral		SYMBOL_NAME(trap)	| jump to trap handler
 
 |
 | _060_real_fpu_disabled():
@@ -230,11 +231,9 @@
 	.global		_060_real_fpu_disabled
 _060_real_fpu_disabled:
 	move.l		%d0,-(%sp)		| enabled the fpu
-	.long	0x4E7A0808
-	|movec		pcr,d0
+	.long	0x4E7A0808			|movec		pcr,%d0
 	bclr		#0x1,%d0
-	.long	0x4E7B0808
-	|movec		%d0,pcr
+	.long	0x4E7B0808			|movec		%d0,pcr
 	move.l		(%sp)+,%d0
 
 	move.l		0xc(%sp),0x2(%sp)	| set "Current PC"
@@ -251,7 +250,7 @@
 |
 	.global		_060_real_trap
 _060_real_trap:
-	rte
+	bral		SYMBOL_NAME(trap)	| jump to trap handler
 
 |############################################################################
 
diff -urN pc-2.0.21/arch/m68k/ifpsp060/iskeleton.S linux-2.0.21/arch/m68k/ifpsp060/iskeleton.S
--- pc-2.0.21/arch/m68k/ifpsp060/iskeleton.S	Fri Apr 26 11:12:35 1996
+++ linux-2.0.21/arch/m68k/ifpsp060/iskeleton.S	Mon Aug 26 23:55:01 1996
@@ -34,6 +34,15 @@
 |	(3) example "Call-out" table
 |
 
+#include <linux/linkage.h>
+
+LOFF_ORIG_D0	= 0x20
+
+#define SAVE_ALL				\
+	clrl	%sp@-;     /* stk_adj */	\
+	movel	%d0,%sp@-; /* orig d0 */	\
+	movel	%d0,%sp@-; /* d0 */		\
+	moveml	%d1-%d5/%a0-%a1,%sp@-
 
 |################################
 | (1) EXAMPLE CALL-OUTS 	#
@@ -61,9 +70,22 @@
 | To simply continue execution at the next instruction, just
 | do an "rte".
 |
+| Linux/68k: If returning to user space, check for needed reselections.
+
 	.global		_060_isp_done
 _060_isp_done:
+	btst	#0x5,%sp@		| supervisor bit set in saved SR?
+	beq	Lnotkern
 	rte
+Lnotkern:
+	tstl	SYMBOL_NAME(need_resched)
+	bne	Lmustsched
+	rte
+Lmustsched:
+	SAVE_ALL
+	moveq	#-1,%d0
+	movel	%d0,%sp@(LOFF_ORIG_D0)    | indicate stack frame not for syscall
+	bral	SYMBOL_NAME(ret_from_exception)	| deliver signals, reschedule etc..
 
 |
 | _060_real_chk():
@@ -71,13 +93,15 @@
 | This is an alternate exit point for the Unimplemented Integer
 | Instruction exception handler. If the instruction was a "chk2"
 | and the operand was out of bounds, then _isp_unimp() creates
-| a CHK exception stack frame from the Unimplemented Integer Instruction
+| a CHK exception stack frame from the Unimplemented Integer Instrcution
 | stack frame and branches to this routine.
 |
+| Linux/68k: commented out test for tracing
+
 	.global		_060_real_chk
 _060_real_chk:
-	tst.b		(%sp)			| is tracing enabled?
-	bpls		real_chk_end		| no
+|	tst.b		(%sp)			| is tracing enabled?
+|	bpls		real_chk_end		| no
 
 |
 |	    CHK FRAME		   TRACE FRAME
@@ -92,11 +116,11 @@
 |	*      SR	*	*      SR	*
 |	*****************	*****************
 |
-	move.b		#0x24,0x7(%sp)		| set trace vecno
-	bral		_060_real_trace
+|	move.b		#0x24,0x7(%sp)		| set trace vecno
+|	bral		_060_real_trace
 
 real_chk_end:
-	rte
+	bral		SYMBOL_NAME(trap)	| jump to trap handler
 
 |
 | _060_real_divbyzero:
@@ -112,10 +136,12 @@
 | then it create a Trace exception stack frame from the "chk" exception
 | stack frame and branches to the _real_trace() entry point.
 | 
+| Linux/68k: commented out test for tracing
+
 	.global		_060_real_divbyzero
 _060_real_divbyzero:
-	tst.b		(%sp)			| is tracing enabled?
-	bpls		real_divbyzero_end	| no
+|	tst.b		(%sp)			| is tracing enabled?
+|	bpls		real_divbyzero_end	| no
 
 |
 |	 DIVBYZERO FRAME	   TRACE FRAME
@@ -130,11 +156,11 @@
 |	*      SR	*	*      SR	*
 |	*****************	*****************
 |
-	move.b		#0x24,0x7(%sp)		| set trace vecno
-	bral		_060_real_trace
+|	move.b		#0x24,0x7(%sp)		| set trace vecno
+|	bral		_060_real_trace
 
 real_divbyzero_end:
-	rte
+	bral		SYMBOL_NAME(trap)	| jump to trap handler
 
 |##########################
 
@@ -177,6 +203,9 @@
 | Expected outputs:
 |	d0 = 0 -> success; non-zero -> failure
 |
+| Linux/68k: As long as ints are disabled, no swapping out should
+| occur (hopefully...)
+|
 	.global		_060_real_lock_page
 _060_real_lock_page:
 	clr.l		%d0
@@ -194,6 +223,9 @@
 |	d0 = `xxxxxxff -> supervisor; `xxxxxx00 -> user
 |	d1 = `xxxxxxff -> longword; `xxxxxx00 -> word
 |
+| Linux/68k: As we do no special locking operation, also no unlocking
+| is needed...
+
 	.global		_060_real_unlock_page
 _060_real_unlock_page:
 	clr.l		%d0
diff -urN pc-2.0.21/arch/m68k/ifpsp060/os.S linux-2.0.21/arch/m68k/ifpsp060/os.S
--- pc-2.0.21/arch/m68k/ifpsp060/os.S	Tue Apr 23 16:14:22 1996
+++ linux-2.0.21/arch/m68k/ifpsp060/os.S	Mon Aug 26 23:55:02 1996
@@ -32,6 +32,7 @@
 |	- example "Call-Out"s required by both the ISP and FPSP.
 |
 
+#include <linux/linkage.h>
 
 |################################
 | EXAMPLE CALL-OUTS 		#
@@ -64,6 +65,10 @@
 | The result is that Unix processes are allowed to sleep as a consequence
 | of a page fault during a _copyout.
 |
+| Linux/68k: The _060_[id]mem_{read,write}_{byte,word,long} functions
+| (i.e. all the known length <= 4) are implemented by single moves
+| statements instead of (more expensive) copy{in,out} calls, if
+| working in user space
 
 |
 | _060_dmem_write():
@@ -147,18 +152,12 @@
 _060_dmem_read_byte:
 	btst		#0x5,0x4(%a6)		| check for supervisor state
 	bnes		dmrbs			| supervisor
-dmrbu:	clr.l		-(%sp)			| clear space on stack for result
-	move.l		#0x1,-(%sp)		| pass: # bytes to copy
-	pea		0x7(%sp)		| pass: dst addr (stack)
-	move.l		%a0,-(%sp)		| pass: src addr (user mem)
-	bsr.l		_copyin			| "copy in" the data
-	move.l		%d0,%d1			| return success
-	add.l		#0xc,%sp		| delete params
-	move.l		(%sp)+,%d0		| put answer in d0
-	rts
+dmrbu:	clr.l		%d0			| clear whole longword
+	movs.b		(%a0),%d0		| fetch user byte
+	bras		dmrbr
 dmrbs:	clr.l		%d0			| clear whole longword
 	move.b		(%a0),%d0		| fetch super byte
-	clr.l		%d1			| return success
+dmrbr:	clr.l		%d1			| return success
 	rts
 
 |
@@ -173,22 +172,29 @@
 |	d0 - data word in d0
 |	d1 - 0 = success, !0 = failure
 |
+| _060_imem_read_word():
+| 
+| Read an instruction word from user memory.
+|
+| INPUTS:
+|	a0 - user source address
+| 	0x4(%a6),bit5 - 1 = supervisor mode, 0 = user mode
+| OUTPUTS:
+|	d0 - instruction word in d0
+|	d1 - 0 = success, !0 = failure
+|
 	.global 		_060_dmem_read_word
+	.global 		_060_imem_read_word
 _060_dmem_read_word:
+_060_imem_read_word:
 	btst		#0x5,0x4(%a6)		| check for supervisor state
 	bnes		dmrws			| supervisor
-dmrwu:	clr.l		-(%sp)			| clear space on stack for result
-	move.l		#0x2,-(%sp)		| pass: # bytes to copy
-	pea		0x6(%sp)		| pass: dst addr (stack)
-	move.l		%a0,-(%sp)		| pass: src addr (user mem)
-	bsr.l		_copyin			| "copy in" the data
-	move.l		%d0,%d1			| return success
-	add.l		#0xc,%sp		| delete params
-	move.l		(%sp)+,%d0		| put answer in d0
-	rts
+dmrwu:	clr.l		%d0			| clear whole longword
+	movs.w		(%a0), %d0		| fetch user word
+	bras		dmrwr
 dmrws:	clr.l		%d0			| clear whole longword
 	move.w		(%a0), %d0		| fetch super word
-	clr.l		%d1			| return success
+dmrwr:	clr.l		%d1			| return success
 	rts
 
 |
@@ -203,21 +209,27 @@
 |	d0 - data longword in d0
 |	d1 - 0 = success, !0 = failure
 |
+| _060_imem_read_long():
+| 
+| Read an instruction longword from user memory.
+|
+| INPUTS:
+|	a0 - user source address
+| 	0x4(%a6),bit5 - 1 = supervisor mode, 0 = user mode
+| OUTPUTS:
+|	d0 - instruction longword in d0
+|	d1 - 0 = success, !0 = failure
+|
 	.global 		_060_dmem_read_long
+	.global 		_060_imem_read_long
 _060_dmem_read_long:
+_060_imem_read_long:
 	btst		#0x5,0x4(%a6)		| check for supervisor state
 	bnes		dmrls			| supervisor
-dmrlu:	subq.l		#0x4,%sp		| clear space on stack for result
-	move.l		#0x4,-(%sp)		| pass: # bytes to copy
-	pea		0x4(%sp)		| pass: dst addr (stack)
-	move.l		%a0,-(%sp)		| pass: src addr (user mem)
-	bsr.l		_copyin			| "copy in" the data
-	move.l		%d0,%d1			| return success
-	add.l		#0xc,%sp		| delete params
-	move.l		(%sp)+,%d0		| put answer in d0
-	rts
+dmrlu:	movs.l		(%a0),%d0		| fetch user longword
+	bras		dmrlr
 dmrls:	move.l		(%a0),%d0		| fetch super longword
-	clr.l		%d1			| return success
+dmrlr:	clr.l		%d1			| return success
 	rts
 
 |
@@ -236,16 +248,10 @@
 _060_dmem_write_byte:
 	btst		#0x5,0x4(%a6)		| check for supervisor state
 	bnes		dmwbs			| supervisor
-dmwbu:	move.l		%d0,-(%sp)		| put src on stack
-	move.l		#0x1,-(%sp)		| pass: # bytes to copy
-	move.l		%a0,-(%sp)		| pass: dst addr (user mem)
-	pea		0xb(%sp)		| pass: src addr (stack)
-	bsr.l		_copyout		| "copy out" the data
-	move.l		%d0,%d1			| return success
-	add.l		#0x10,%sp		| delete params + src
-	rts
+dmwbu:	movs.b		%d0,(%a0)		| store user byte
+	bras		dmwbr
 dmwbs:	move.b		%d0,(%a0)		| store super byte
-	clr.l		%d1			| return success
+dmwbr:	clr.l		%d1			| return success
 	rts
 
 |
@@ -264,16 +270,10 @@
 _060_dmem_write_word:
 	btst		#0x5,0x4(%a6)		| check for supervisor state
 	bnes		dmwws			| supervisor
-dmwwu:	move.l		%d0,-(%sp)		| put src on stack
-	move.l		#0x2,-(%sp)		| pass: # bytes to copy
-	move.l		%a0,-(%sp)		| pass: dst addr (user mem)
-	pea		0xa(%sp)		| pass: src addr (stack)
-	bsr.l		_copyout		| "copy out" the data
-	move.l		%d0,%d1			| return success
-	add.l		#0x10,%sp		| delete params + src
-	rts
+dmwwu:	movs.w		%d0,(%a0)		| store user word
+	bras		dmwwr
 dmwws:	move.w		%d0,(%a0)		| store super word
-	clr.l		%d1			| return success
+dmwwr:	clr.l		%d1			| return success
 	rts
 
 |
@@ -292,75 +292,12 @@
 _060_dmem_write_long:
 	btst		#0x5,0x4(%a6)		| check for supervisor state
 	bnes		dmwls			| supervisor
-dmwlu:	move.l		%d0,-(%sp)		| put src on stack
-	move.l		#0x4,-(%sp)		| pass: # bytes to copy
-	move.l		%a0,-(%sp)		| pass: dst addr (user mem)
-	pea		0x8(%sp)		| pass: src addr (stack)
-	bsr.l		_copyout		| "copy out" the data
-	move.l		%d0,%d1			| return success
-	add.l		#0x10,%sp		| delete params + src
-	rts
+dmwlu:	movs.l		%d0,(%a0)		| store user longword
+	bra		dmwlr
 dmwls:	move.l		%d0,(%a0)		| store super longword
-	clr.l		%d1			| return success
-	rts
-
-|
-| _060_imem_read_word():
-| 
-| Read an instruction word from user memory.
-|
-| INPUTS:
-|	a0 - user source address
-| 	0x4(%a6),bit5 - 1 = supervisor mode, 0 = user mode
-| OUTPUTS:
-|	d0 - instruction word in d0
-|	d1 - 0 = success, !0 = failure
-|
-	.global 		_060_imem_read_word
-_060_imem_read_word:
-	btst		#0x5,0x4(%a6)		| check for supervisor state
-	bnes		imrws			| supervisor
-imrwu:	clr.l		-(%sp)			| clear space on stack for result
-	move.l		#0x2,-(%sp)		| pass: # bytes to copy
-	pea		0x6(%sp)		| pass: dst addr (stack)
-	move.l		%a0,-(%sp)		| pass: src addr (user mem)
-	bsr.l		_copyin			| "copy in" the data
-	move.l		%d0,%d1			| return success
-	add.l		#0xc,%sp		| delete params
-	move.l		(%sp)+,%d0		| put answer in d0
-	rts
-imrws:	move.w		(%a0),%d0		| fetch super word
-	clr.l		%d1			| return success
+dmwlr:	clr.l		%d1			| return success
 	rts
 
-|
-| _060_imem_read_long():
-| 
-| Read an instruction longword from user memory.
-|
-| INPUTS:
-|	a0 - user source address
-| 	0x4(%a6),bit5 - 1 = supervisor mode, 0 = user mode
-| OUTPUTS:
-|	d0 - instruction longword in d0
-|	d1 - 0 = success, !0 = failure
-|
-	.global 		_060_imem_read_long
-_060_imem_read_long:
-	btst		#0x5,0x4(%a6)		| check for supervisor state
-	bnes		imrls			| supervisor
-imrlu:	subq.l		#0x4,%sp		| clear space on stack for result
-	move.l		#0x4,-(%sp)		| pass: # bytes to copy
-	pea		0x4(%sp)		| pass: dst addr (stack)
-	move.l		%a0,-(%sp)		| pass: src addr (user mem)
-	bsr.l		_copyin			| "copy in" the data
-	move.l		%d0,%d1			| return success
-	add.l		#0xc,%sp		| delete params
-	move.l		(%sp)+,%d0		| put answer in d0
-	rts
-imrls:	move.l		(%a0),%d0		| fetch super longword
-	clr.l		%d1			| return success
-	rts
 
 |###############################################
 
@@ -369,6 +306,10 @@
 | Assumes that D0/D1/A0/A1 are scratch registers. The _copyin/_copyout
 | below assume that the SFC/DFC have been set previously.
 |
+| Linux/68k: These are basically non-inlined versions of
+| memcpy_{to,from}fs, but without long-transfer optimization
+| Note: Assumed that SFC/DFC are pointing correctly to user data
+| space... Should be right, or are there any exceptions?
 
 |
 | int _copyout(supervisor_addr, user_addr, nbytes)
@@ -378,11 +319,12 @@
 	move.l		4(%sp),%a0		| source
 	move.l		8(%sp),%a1		| destination
 	move.l		12(%sp),%d0		| count
+    subq.l      #1,%d0
 moreout:
 	move.b		(%a0)+,%d1		| fetch supervisor byte
 	movs.b		%d1,(%a1)+		| store user byte
-	subq.l		#0x1,%d0		| are we through yet?
-	bne		moreout			| no; so, continue
+	dbra		%d0,moreout		| are we through yet?
+    moveq		#0,%d0			| return success
 	rts
 
 |
@@ -393,11 +335,13 @@
 	move.l		4(%sp),%a0		| source
 	move.l		8(%sp),%a1		| destination
 	move.l		12(%sp),%d0		| count
+    subq.l      #1,%d0
 morein:
 	movs.b		(%a0)+,%d1		| fetch user byte
 	move.b		%d1,(%a1)+		| write supervisor byte
 	subq.l		#0x1,%d0		| are we through yet?
-	bne		morein			| no; so, continue
+	dbra		%d0,morein		| are we through yet?
+    moveq		#0,%d0			| return success
 	rts
 
 |###########################################################################
@@ -413,7 +357,7 @@
 |
 	.global		_060_real_trace
 _060_real_trace:
-	rte
+	bral	SYMBOL_NAME(trap)
 
 |
 | _060_real_access():
@@ -429,4 +373,4 @@
 |
 	.global		_060_real_access
 _060_real_access:
-	rte
+	bral	SYMBOL_NAME(buserr)
diff -urN pc-2.0.21/arch/m68k/kernel/Makefile linux-2.0.21/arch/m68k/kernel/Makefile
--- pc-2.0.21/arch/m68k/kernel/Makefile	Mon Apr  1 20:46:31 1996
+++ linux-2.0.21/arch/m68k/kernel/Makefile	Sat Aug 24 14:30:44 1996
@@ -8,13 +8,12 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 .S.o:
-	$(CC) -D__ASSEMBLY__ -traditional -Wa,-m68030 -c $< -o $*.o
+	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
 
 all: kernel.o head.o
 O_TARGET := kernel.o
 O_OBJS := entry.o process.o traps.o ints.o signal.o ptrace.o \
-	  setup.o bios32.o sys_m68k.o console.o time.o
-OX_OBJS = ksyms.o
+	  setup.o bios32.o sys_m68k.o console.o time.o ksyms.o
 
 head.o: head.S
 
diff -urN pc-2.0.21/arch/m68k/kernel/console.c linux-2.0.21/arch/m68k/kernel/console.c
--- pc-2.0.21/arch/m68k/kernel/console.c	Mon May  6 11:26:02 1996
+++ linux-2.0.21/arch/m68k/kernel/console.c	Wed Sep 18 00:42:32 1996
@@ -80,7 +80,7 @@
  */
 
 #define BLANK 0x0020
-#undef CAN_LOAD_EGA_FONTS    /* undefine if the user must not do this */
+#define CAN_LOAD_EGA_FONTS    /* undefine if the user must not do this */
 
 /* A bitmap for codes <32. A bit of 1 indicates that the code
  * corresponding to that bit number invokes some special action
@@ -175,6 +175,8 @@
        int do_poke_blanked_console = 0;
        int console_blanked = 0;
 static int blankinterval = 10*60*HZ;
+static int vesa_off_interval = 0;
+static int vesa_blank_mode = 0; /* 0:none 1:suspendV 2:suspendH 3:powerdown */
 
 static struct vc {
 	struct vc_data *d;
@@ -537,7 +539,7 @@
  */
 static void gotoxy(int currcons, int new_x, int new_y)
 {
-	int max_y;
+	int min_y, max_y;
 
 	if (new_x < 0)
 		x = 0;
@@ -547,21 +549,28 @@
 		else
 			x = new_x;
  	if (decom) {
-		new_y += top;
+		min_y = top;
 		max_y = bottom;
-	} else
+	} else {
+		min_y = 0;
 		max_y = rows;
-	if (new_y < 0)
-		y = 0;
+	}
+	if (new_y < min_y)
+		y = min_y;
+	else if (new_y >= max_y)
+		y = max_y - 1;
 	else
-		if (new_y >= max_y)
-			y = max_y - 1;
-		else
-			y = new_y;
+		y = new_y;
 	pos = video_mem_start + y * cols + x;
 	need_wrap = 0;
 }
 
+/* for absolute user moves, when decom is set */
+static void gotoxay(int currcons, int new_x, int new_y)
+{
+	gotoxy(currcons, new_x, decom ? (top+new_y) : new_y);
+}
+
 static void hide_cursor(int currcons)
 {
 	sw->con_cursor(vc_cons[currcons].d,CM_ERASE);
@@ -1144,7 +1153,7 @@
 				break;
 			case 6:			/* Origin relative/absolute */
 				decom = on_off;
-				gotoxy(currcons,0,0);
+				gotoxay(currcons,0,0);
 				break;
 			case 7:			/* Autowrap on/off */
 				decawm = on_off;
@@ -1227,6 +1236,9 @@
 		case 13: /* unblank the screen */
 			unblank_screen();
 			break;
+		case 14: /* set vesa powerdown interval */
+			vesa_off_interval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
+			break;
 	}
 }
 
@@ -1516,34 +1528,34 @@
 		    tc = translate[toggle_meta ? (c|0x80) : c];
 		}
 
-		/* If the original code was < 32 we only allow a
-		 * glyph to be displayed if the code is not normally
-		 * used (such as for cursor movement) or if the
-		 * disp_ctrl mode has been explicitly enabled.
-		 * Note: ESC is *never* allowed to be displayed as
-		 * that would disable all escape sequences!
-		 * To display font position 0x1B, go into UTF mode
-		 * and display character U+F01B, or change the mapping.
-		 */
-		ok = (tc && (c >= 32 || (!utf && !(((disp_ctrl ? CTRL_ALWAYS
-					    : CTRL_ACTION) >> c) & 1))));
+                /* If the original code was a control character we
+                 * only allow a glyph to be displayed if the code is
+                 * not normally used (such as for cursor movement) or
+                 * if the disp_ctrl mode has been explicitly enabled.
+                 * Certain characters (as given by the CTRL_ALWAYS
+                 * bitmap) are always displayed as control characters,
+                 * as the console would be pretty useless without
+                 * them; to display an arbitrary font position use the
+                 * direct-to-font zone in UTF-8 mode.
+                 */
+                ok = tc && (c >= 32 ||
+                            (!utf && !(((disp_ctrl ? CTRL_ALWAYS
+                                         : CTRL_ACTION) >> c) & 1)))
+                        && (c != 127 || disp_ctrl);
 
 		if (vc_state == ESnormal && ok) {
 			/* Now try to find out how to display it */
 			tc = conv_uni_to_pc(tc);
-			if ( tc == -4 )
-			  {
-			    /* If we got -4 (not found) then see if we have
-			       defined a replacement character (U+FFFD) */
-			    tc = conv_uni_to_pc(0xfffd);
-			  }
-			else if ( tc == -3 )
-			  {
-			    /* Bad hash table -- hope for the best */
-			    tc = c;
-			  }
+			if ( tc == -4 ) {
+                                /* If we got -4 (not found) then see if we have
+                                   defined a replacement character (U+FFFD) */
+                                tc = conv_uni_to_pc(0xfffd);
+                        } else if ( tc == -3 ) {
+                                /* Bad hash table -- hope for the best */
+                                tc = c;
+                        }
 			if (tc & ~console_charmask)
-			  continue; /* Conversion failed */
+				continue; /* Conversion failed */
 
 			if (need_wrap) {
 				cr(currcons);
@@ -1851,12 +1863,12 @@
 				continue;
 			    case 'd':
 				if (par[0]) par[0]--;
-				gotoxy(currcons,x,par[0]);
+				gotoxay(currcons,x,par[0]);
 				continue;
 			    case 'H': case 'f':
 				if (par[0]) par[0]--;
 				if (par[1]) par[1]--;
-				gotoxy(currcons,par[1],par[0]);
+				gotoxay(currcons,par[1],par[0]);
 				continue;
 			    case 'J':
 				csi_J(currcons,par[0]);
@@ -1907,7 +1919,7 @@
 				    par[1] <= rows) {
 					top=par[0]-1;
 					bottom=par[1];
-					gotoxy(currcons,0,0);
+					gotoxay(currcons,0,0);
 				}
 				continue;
 			    case 's':
@@ -2258,6 +2270,29 @@
 	return kmem_start;
 }
 
+void vesa_powerdown_screen(void)
+{
+	int currcons = fg_console;
+
+	timer_active &= ~(1<<BLANK_TIMER);
+	timer_table[BLANK_TIMER].fn = unblank_screen;
+
+	/* Power down if currently suspended (1 or 2),
+	 * suspend if currently blanked (0),
+	 * else do nothing (i.e. already powered down (3)).
+	 * Called only if powerdown features are allowed.
+	 */
+	switch (vesa_blank_mode) {
+	case 0:
+		sw->con_blank(2);
+		break;
+	case 1:
+	case 2:
+		sw->con_blank(4);
+		break;
+	}
+}
+
 void do_blank_screen(int nopowersave)
 {
 	int currcons;
@@ -2273,14 +2308,22 @@
 
 	/* don't blank graphics */
 	if (vt_cons[fg_console]->vc_mode == KD_TEXT) {
-		timer_active &= ~(1<<BLANK_TIMER);
-		timer_table[BLANK_TIMER].fn = unblank_screen;
+		if (vesa_off_interval && !nopowersave) {
+			timer_table[BLANK_TIMER].fn = vesa_powerdown_screen;
+			timer_table[BLANK_TIMER].expires = jiffies + vesa_off_interval;
+			timer_active |= (1<<BLANK_TIMER);
+		} else {
+			timer_active &= ~(1<<BLANK_TIMER);
+			timer_table[BLANK_TIMER].fn = unblank_screen;
+		}
 
 		/* try not to lose information by blanking,
 		   and not to waste memory */
 		currcons = fg_console;
 		has_scrolled = 0;
-		sw->con_blank (1);
+		sw->con_blank(1);
+		if (!nopowersave)
+			sw->con_blank(vesa_blank_mode + 1);
 	}
 	else
 		hide_cursor(fg_console);
@@ -2413,89 +2456,56 @@
 /*
  * PIO_FONT support.
  *
- * The font loading code goes back to the codepage package by
- * Joel Hoffman (joel@wam.umd.edu). (He reports that the original
- * reference is: "From: p. 307 of _Programmer's Guide to PC & PS/2
- * Video Systems_ by Richard Wilton. 1987.  Microsoft Press".)
- *
- * Change for certain monochrome monitors by Yury Shevchuck
- * (sizif@botik.yaroslavl.su).
+ * Currently we only support 8 pixels wide fonts, at a maximum height
+ * of 32 pixels. Userspace fontdata is stored with 32 bytes reserved
+ * for each character which is kinda wasty, but this is done in order
+ * to maintain compatibility with the EGA/VGA fonts. It is upto the
+ * actual low-level console-driver convert data into its favorite
+ * format (maybe we should add a `fontoffset' field to the `display'
+ * structure so we wont have to convert the fontdata all the time.
+ * /Jes
  */
 
-#define colourmap ((char *)0xa0000)
-/* Pauline Middelink <middelin@polyware.iaf.nl> reports that we
-   should use 0xA0000 for the bwmap as well.. */
-#define blackwmap ((char *)0xa0000)
 #define cmapsz 8192
-#define seq_port_reg (0x3c4)
-#define seq_port_val (0x3c5)
-#define gr_port_reg (0x3ce)
-#define gr_port_val (0x3cf)
 
-static int set_get_font(char * arg, int set)
+static int set_get_font(char * arg, int set, int ch512)
 {
 #ifdef CAN_LOAD_EGA_FONTS
-	int i;
+	int i, unit, size;
 	char *charmap;
-	int beg;
-
-	/* no use to "load" CGA... */
 
-	if (video_type == VIDEO_TYPE_EGAC) {
-		charmap = colourmap;
-		beg = 0x0e;
-	} else if (video_type == VIDEO_TYPE_EGAM) {
-		charmap = blackwmap;
-		beg = 0x0a;
-	} else
+	if (arg){
+		i = verify_area(set ? VERIFY_READ : VERIFY_WRITE,
+				(void *)arg, ch512 ? 2*cmapsz : cmapsz);
+		if (i)
+			return i;
+	}else
 		return -EINVAL;
 
-	i = verify_area(set ? VERIFY_READ : VERIFY_WRITE, (void *)arg, cmapsz);
-	if (i)
-		return i;
 
-	cli();
-	outb_p( 0x00, seq_port_reg );   /* First, the sequencer */
-	outb_p( 0x01, seq_port_val );   /* Synchronous reset */
-	outb_p( 0x02, seq_port_reg );
-	outb_p( 0x04, seq_port_val );   /* CPU writes only to map 2 */
-	outb_p( 0x04, seq_port_reg );
-	outb_p( 0x07, seq_port_val );   /* Sequential addressing */
-	outb_p( 0x00, seq_port_reg );
-	outb_p( 0x03, seq_port_val );   /* Clear synchronous reset */
-
-	outb_p( 0x04, gr_port_reg );    /* Now, the graphics controller */
-	outb_p( 0x02, gr_port_val );    /* select map 2 */
-	outb_p( 0x05, gr_port_reg );
-	outb_p( 0x00, gr_port_val );    /* disable odd-even addressing */
-	outb_p( 0x06, gr_port_reg );
-	outb_p( 0x00, gr_port_val );    /* map start at A000:0000 */
-	sti();
+	size = ch512 ? 2*cmapsz : cmapsz;
 
-	if (set)
-		memcpy_fromfs (charmap, arg, cmapsz);
-	else
-		memcpy_tofs (arg, charmap, cmapsz);
+	charmap = (char *)kmalloc(size, GFP_USER);
 
-	cli();
-	outb_p( 0x00, seq_port_reg );   /* First, the sequencer */
-	outb_p( 0x01, seq_port_val );   /* Synchronous reset */
-	outb_p( 0x02, seq_port_reg );
-	outb_p( 0x03, seq_port_val );   /* CPU writes to maps 0 and 1 */
-	outb_p( 0x04, seq_port_reg );
-	outb_p( 0x03, seq_port_val );   /* odd-even addressing */
-	outb_p( 0x00, seq_port_reg );
-	outb_p( 0x03, seq_port_val );   /* clear synchronous reset */
-
-	outb_p( 0x04, gr_port_reg );    /* Now, the graphics controller */
-	outb_p( 0x00, gr_port_val );    /* select map 0 for CPU */
-	outb_p( 0x05, gr_port_reg );
-	outb_p( 0x10, gr_port_val );    /* enable even-odd addressing */
-	outb_p( 0x06, gr_port_reg );
-	outb_p( beg, gr_port_val );     /* map starts at b800:0 or b000:0 */
-	sti();
+	if (set){
+		memcpy_fromfs(charmap, arg, size);
 
-	return 0;
+		for (unit = 32; unit > 0; unit--)
+			for (i = 0; i < (ch512 ? 512 : 256); i++)
+				if (charmap[32*i+unit-1])
+					goto nonzero;
+	nonzero:
+		i = conswitchp->con_set_font(vc_cons[fg_console].d, 8,
+					     unit, charmap);
+	}else{
+		memset(charmap, 0, size);
+		i = conswitchp->con_get_font(vc_cons[fg_console].d,
+					     &unit, &unit, charmap);
+		memcpy_tofs(arg, charmap, size);
+	}
+	kfree(charmap);
+
+	return i;
 #else
 	return -EINVAL;
 #endif
@@ -2530,15 +2540,22 @@
  * 8xH fonts (0 < H <= 32).
  */
 
-int con_set_font (char *arg)
+int con_set_font (char *arg, int ch512)
 {
-	hashtable_contents_valid = 0;
-	return set_get_font (arg,1);
+	int i;
+
+	i = set_get_font (arg,1,ch512);
+  	if ( !i ) {
+		hashtable_contents_valid = 0;
+      		video_mode_512ch = ch512;
+      		console_charmask = ch512 ? 0x1ff : 0x0ff;
+	}
+	return i;
 }
 
 int con_get_font (char *arg)
 {
-	return set_get_font (arg,0);
+	return set_get_font (arg,0,video_mode_512ch);
 }
 
 /*
@@ -2554,6 +2571,9 @@
 
 void set_vesa_blanking(int arg)
 {
+	char *argp = (char *)arg + 1;
+	unsigned int mode = get_fs_byte(argp);
+	vesa_blank_mode = (mode < 4) ? mode : 0;
 }
 
 unsigned long get_video_num_lines(unsigned int currcons)
diff -urN pc-2.0.21/arch/m68k/kernel/entry.S linux-2.0.21/arch/m68k/kernel/entry.S
--- pc-2.0.21/arch/m68k/kernel/entry.S	Thu May 16 08:05:10 1996
+++ linux-2.0.21/arch/m68k/kernel/entry.S	Mon Sep 23 00:40:04 1996
@@ -8,7 +8,9 @@
  * License.  See the file README.legal in the main directory of this archive
  * for more details.
  *
- * 680x0 support by Hamish Macdonald
+ * Linux/m68k support by Hamish Macdonald
+ *
+ * 68060 fixes by Jesper Skov
  *
  */
 
@@ -46,11 +48,11 @@
  */
 
 #include <linux/sys.h>
-
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/setup.h>
 #include <asm/segment.h>
 
-LCF_MASK	= 0x0001
-
 LENOSYS = 38
 
 /*
@@ -62,18 +64,16 @@
 LTASK_SIGNAL	= 12
 LTASK_BLOCKED	= 16
 LTASK_FLAGS	= 20
-LTASK_ERRNO	= 24
-
-#include <linux/config.h>
-#include <linux/linkage.h>
 
 /* the following macro is used when enabling interrupts */
-#if defined(CONFIG_ATARI) && !defined(CONFIG_AMIGA) && !defined(CONFIG_MAC)
+#if defined(CONFIG_ATARI_ONLY)
 	/* block out HSYNC on the atari */
 #define ALLOWINT 0xfbff
+#define	MAX_NOINT_IPL	3
 #else
 	/* portable version */
 #define ALLOWINT 0xf8ff
+#define	MAX_NOINT_IPL	0
 #endif /* machine compilation types */ 
 
 LD0		= 0x1C
@@ -164,23 +164,12 @@
 	lea	SYMBOL_NAME(sys_call_table),%a0
 	movel	%a0@(%d2:l:4),%d3
 	jeq	SYMBOL_NAME(ret_from_exception)
-	andw	#~LCF_MASK,%sp@(LSR)	| assume syscall success
 	movel	SYMBOL_NAME(current_set),%a0
-	clrl	%a0@(LTASK_ERRNO)
 	btst	#5,%a0@(LTASK_FLAGS+3)	| PF_TRACESYS
 	bnes	1f
 	movel	%d3,%a0
 	jbsr	%a0@
 	movel	%d0,%sp@(LD0)		| save the return value
-	jpl	2f
-	orw	#LCF_MASK,%sp@(LSR)	| set carry to indicate error
-2:
-	movel	SYMBOL_NAME(current_set),%a0
-	movel	%a0@(LTASK_ERRNO),%d1
-	negl	%d1
-	jeq	SYMBOL_NAME(ret_from_exception)
-	movel	%d1,%sp@(LD0)
-	orw	#LCF_MASK,%sp@(LSR)	| set carry to indicate error
 	jra	SYMBOL_NAME(ret_from_exception)
 1:
 	subql	#4,%sp
@@ -191,16 +180,7 @@
 	movel	%d3,%a0
 	jbsr	%a0@
 	movel	%d0,%sp@(LD0)		| save the return value
-	jpl	2f
-	orw	#LCF_MASK,%sp@(LSR)	| set carry to indicate error
-2:
-	movel	SYMBOL_NAME(current_set),%a0
-	movel	%a0@(LTASK_ERRNO),%d1
-	negl	%d1
-	jeq	2f
-	movel	%d1,%sp@(LD0)
-	orw	#LCF_MASK,%sp@(LSR)	| set carry to indicate error
-2:	subql	#4,%sp			| dummy return address
+	subql	#4,%sp			| dummy return address
 	SAVE_SWITCH_STACK
 	jbsr	SYMBOL_NAME(syscall_trace)
 
@@ -225,19 +205,21 @@
 	jbsr	SYMBOL_NAME(send_sig)
 	addql	#8,%sp
 	addql	#4,%sp
+	movel	SYMBOL_NAME(current_set),%a0
 
 1:
-	moveq	#0,%d0
-	movel	SYMBOL_NAME(current_set),%a0
-	cmpl	%a0@(LTASK_STATE),%d0     | state
+	tstl	%a0@(LTASK_STATE)	| state
 	jne	SYMBOL_NAME(reschedule)
-	cmpl	%a0@(LTASK_COUNTER),%d0   | counter
+	tstl	%a0@(LTASK_COUNTER)	| counter
 	jeq	SYMBOL_NAME(reschedule)
 
 	movel	%a0@(LTASK_BLOCKED),%d0
 	movel	%d0,%d1			| save blocked in d1 for sig handling
 	notl	%d0
-	andl	%a0@(LTASK_SIGNAL),%d0
+	btst	#4,%a0@(LTASK_FLAGS+3)	| PF_PTRACED
+	jeq	1f
+	moveq	#-1,%d0			| let the debugger see all signals
+1:	andl	%a0@(LTASK_SIGNAL),%d0
 	jne	Lsignal_return
 2:	RESTORE_ALL
 
@@ -261,13 +243,11 @@
 	movel	%d0,%sp@(LORIG_D0)	| a -1 in the ORIG_D0 field
 					| signifies that the stack frame
 					| is NOT for syscall
-	
 	addql	#1,SYMBOL_NAME(intr_count)
+					|  put exception # in d0
+	bfextu %sp@(LFORMATVEC){#4,#10},%d0
 
-	movew	%sp@(LFORMATVEC),%d0      |  put exception # in d0
-	andil	#0xfff,%d0		|  mask out format nybble
-
-	movel	%sp,%sp@-		
+	movel	%sp,%sp@-
 	movel	%d0,%sp@- 		|  put vector # on stack
 	jbsr	SYMBOL_NAME(process_int)|  process the IRQ
 	addql	#8,%sp			|  pop parameters off stack
@@ -275,27 +255,27 @@
 SYMBOL_NAME_LABEL(ret_from_interrupt)
 	/* check if we need to do software interrupts */
 1:
-	movel	SYMBOL_NAME(intr_count),%d2
-	subql	#1,%d2
-	jne	2f
-
+	movel	SYMBOL_NAME(intr_count),%d1
+	subql	#1,%d1
+	jne	4f
+	bfextu  %sp@(LSR){#5,#3},%d0    | Check for nested interrupt.
+#if MAX_NOINT_IPL > 0
+	cmpiw	#MAX_NOINT_IPL,%d0
+#endif
+	jhi	4f
+2:
 	movel	SYMBOL_NAME(bh_active),%d0
 	andl	SYMBOL_NAME(bh_mask),%d0
 	jne	3f
 
-	movel	%d2,SYMBOL_NAME(intr_count)
-
+	clrl	SYMBOL_NAME(intr_count)	| deliver signals, reschedule etc..
 	jra	SYMBOL_NAME(ret_from_exception)
-					| deliver signals, reschedule etc..
-
-2:	movel	%d2,SYMBOL_NAME(intr_count)
-	RESTORE_ALL
-3:
-	movew	%sr,%sp@-
-	andiw	#(ALLOWINT),%sr		| allow interrupts
+3:	
 	jbsr	SYMBOL_NAME(do_bottom_half)
-	movew	%sp@+,%sr
-	jra	1b
+	jbra	2b
+4:
+	movel	%d1,SYMBOL_NAME(intr_count)
+	RESTORE_ALL
 
 
 /* Handler for uninitialized and spurious interrupts */
@@ -335,7 +315,6 @@
 	rts
 
 LFLUSH_I_AND_D = 0x00000808
-LBI_CPU = 4
 LTSS_KSP	= 0
 LTSS_USP	= 4
 LTSS_SR		= 8
@@ -345,17 +324,16 @@
 
 SYMBOL_NAME_LABEL(resume)
 	/*
-	 * Beware - when entering resume, offset of tss is in a1 and
-	 * next (the new task) is in d1, so don't chance these
-	 * registers before their contents have been used.
+	 * Beware - when entering resume, offset of tss is in d1,
+	 * prev (the current task) is in a0, next (the new task)
+	 * is in a1 and d2.b is non-zero if the mm structure is
+	 * shared between the tasks, so don't change these
+	 * registers until their contents are no longer needed.
 	 */
-	
-	/* current tasks task_struct */
- 	movel	SYMBOL_NAME(current_set),%a0
 
 	/* offset of tss struct (processor state) from beginning
 	   of task struct */
-	addl	%a1,%a0
+	addl	%d1,%a0
 
 	/* save sr */
 	movew	%sr,%a0@(LTSS_SR)
@@ -372,11 +350,6 @@
 	movec	%usp,%d0
 	movel	%d0,%a0@(LTSS_USP)
 
-	/* load new task (before adjusting stack) */
-	/* The task has already been put in d1 by switch_to (Jes) */
-/*
-	movel	%sp@(4),%d1
-*/
 	/* save non-scratch registers on stack */
 	SAVE_SWITCH_STACK
 
@@ -385,41 +358,59 @@
 
 	/* save floating point context */
 	fsave	%a0@(LTSS_FPCTXT+27*4)
-	tstb	%a0@(LTSS_FPCTXT+27*4)
-	jeq	1f
-	fmovemx	%fp0-%fp7,%a0@(LTSS_FPCTXT)
+
+#if defined(CONFIG_M68060)
+#if !defined(CONFIG_M68060_ONLY)
+	btst	#3,SYMBOL_NAME(boot_info)+BI_cputype+3
+	beqs	1f
+#endif
+	/* The 060 FPU keeps status in bits 15-8 of the first longword */
+	tstb	%a0@(LTSS_FPCTXT+27*4+2)
+	jeq	3f
+#if !defined(CONFIG_M68060_ONLY)
+	jra	2f
+#endif
+#endif /* CONFIG_M68060 */
+#if !defined(CONFIG_M68060_ONLY)
+1:	tstb	%a0@(LTSS_FPCTXT+27*4)
+	jeq	3f
+#endif
+2:	fmovemx	%fp0-%fp7,%a0@(LTSS_FPCTXT)
 	fmoveml	%fpcr/%fpsr/%fpiar,%a0@(LTSS_FPCTXT+24*4)
-1:
+3:
 
-	/* get pointer to tss struct (d1 contains new task) */
-	movel	%d1,SYMBOL_NAME(current_set)
-	movel	%d1,%a0
-	addl	%a1,%a0
+	/* get pointer to tss struct (a1 contains new task) */
+	movel	%a1,SYMBOL_NAME(current_set)
+	addl	%d1,%a1
+
+	/* Skip address space switching if they are the same. */
+	tstb	%d2
+	jne	4f
 
+#if defined(CONFIG_M68020_OR_M68030) && defined(CONFIG_M68040_OR_M68060)
 	/* 68040 or 68060 ? */
-	btst	#2,SYMBOL_NAME(boot_info)+LBI_CPU+3
-	bnes	1f
-	btst	#3,SYMBOL_NAME(boot_info)+LBI_CPU+3
+	tstl	SYMBOL_NAME(m68k_is040or060)
 	bnes	1f
-
+#endif
+#if defined(CONFIG_M68020_OR_M68030)
 	/*
 	 * switch address space
 	 */
 
 	/* flush MC68030/MC68020 caches (they are virtually addressed) */
 	movec	%cacr,%d0
-	oril	#LFLUSH_I_AND_D,%d0
+	oriw	#LFLUSH_I_AND_D,%d0
 	movec	%d0,%cacr
 
 	/* switch the root pointer */
-	pmove	%a0@(LTSS_CRP),%crp
-
-	/* flush address translation cache (probably not needed */
-	pflusha
+	pmove	%a1@(LTSS_CRP),%crp
+#endif
 
+#if defined(CONFIG_M68020_OR_M68030) && defined(CONFIG_M68040_OR_M68060)
 	jra	2f	/* skip m68040 stuff */
-
 1:
+#endif
+#if defined(CONFIG_M68040_OR_M68060)
 	/*
 	 * switch address space
 	 */
@@ -428,42 +419,60 @@
 	.word	0xf510			/* pflushan */
 
 	/* switch the root pointer */
-	movel	%a0@(LTSS_CRP+4),%d0
+	movel	%a1@(LTSS_CRP+4),%d0
 	.long	0x4e7b0806		/* movec d0,urp */
 
+#if defined (CONFIG_M68060)
 	/* is it a '060 ? */
-	btst	#3,SYMBOL_NAME(boot_info)+LBI_CPU+3
+	btst	#3,SYMBOL_NAME(boot_info)+BI_cputype+3
 	beqs	2f
 	/* clear user entries in the branch cache */
 	movec	%cacr,%d0
 	orl	#0x00200000,%d0
 	movec	%d0,%cacr
-
+#endif /* CONFIG_M68060 */
+#endif /* CONFIG_M68040_OR_M68060 */
 2:
+4:
 	/* restore floating point context */
-	tstb	%a0@(LTSS_FPCTXT+27*4)
-	jeq	1f
-	fmovemx	%a0@(LTSS_FPCTXT),%fp0-%fp7
-	fmoveml	%a0@(LTSS_FPCTXT+24*4),%fpcr/%fpsr/%fpiar
-1:	frestore %a0@(LTSS_FPCTXT+27*4)
+
+#if defined(CONFIG_M68060)
+#if !defined(CONFIG_M68060_ONLY)
+	btst	#3,SYMBOL_NAME(boot_info)+BI_cputype+3
+	beqs	1f
+#endif
+	/* The 060 FPU keeps status in bits 15-8 of the first longword */
+	tstb	%a1@(LTSS_FPCTXT+27*4+2)
+	jeq	3f
+#if !defined(CONFIG_M68060_ONLY)
+	jra	2f
+#endif
+#endif /* CONFIG_M68060 */
+#if !defined(CONFIG_M68060_ONLY)
+1:	tstb	%a1@(LTSS_FPCTXT+27*4)
+	jeq	3f
+#endif	
+2:	fmovemx	%a1@(LTSS_FPCTXT),%fp0-%fp7
+	fmoveml	%a1@(LTSS_FPCTXT+24*4),%fpcr/%fpsr/%fpiar
+3:	frestore %a1@(LTSS_FPCTXT+27*4)
 
 	/* restore the kernel stack pointer */
-	movel	%a0@(LTSS_KSP),%sp
+	movel	%a1@(LTSS_KSP),%sp
 
 	/* restore non-scratch registers */
 	RESTORE_SWITCH_STACK
 
 	/* restore user stack pointer */
-	movel	%a0@(LTSS_USP),%d0
-	movec	%d0,%usp
+	movel	%a1@(LTSS_USP),%a0
+	movel	%a0,%usp
 
 	/* restore fs (sfc,%dfc) */
-	movew	%a0@(LTSS_FS),%a1
-	movec	%a1,%sfc
-	movec	%a1,%dfc
+	movew	%a1@(LTSS_FS),%a0
+	movec	%a0,%sfc
+	movec	%a0,%dfc
 
 	/* restore status register */
-	movew	%a0@(LTSS_SR),%sr
+	movew	%a1@(LTSS_SR),%sr
 
 	rts
 
diff -urN pc-2.0.21/arch/m68k/kernel/head.S linux-2.0.21/arch/m68k/kernel/head.S
--- pc-2.0.21/arch/m68k/kernel/head.S	Sat May 18 10:15:10 1996
+++ linux-2.0.21/arch/m68k/kernel/head.S	Mon Sep 23 21:50:49 1996
@@ -14,7 +14,7 @@
 ** 94/11/14 Andreas Schwab: put kernel at PAGESIZE
 ** 94/11/18 Andreas Schwab: remove identity mapping of STRAM for Atari
 ** ++ Bjoern & Roman: ATARI-68040 support for the Medusa
-** 96/04/26 G|nther Kelleter: fixed identity mapping for Falcon with
+** 96/04/26 Guenther Kelleter: fixed identity mapping for Falcon with
 ** 			      Magnum- and FX-alternate ram
 **
 ** This file is subject to the terms and conditions of the GNU General Public
@@ -65,9 +65,9 @@
  *   d4 - machine type
  */
 
-#include <linux/autoconf.h>
+#include <linux/config.h>
 #include <linux/linkage.h>
-#include <asm/bootinfo.h>
+#include <asm/setup.h>
 #include <asm/pgtable.h>
 
 .globl SYMBOL_NAME(kernel_pg_dir), SYMBOL_NAME(kpt)
@@ -197,7 +197,7 @@
 	btst	#CPUB_68060,%d0
 	jeq	1f
 	/* '060: d6 := BIT0460|BIT060, cache mode 0x60 (no-cache/non-ser) */
-	movel	#D6F_060+_PAGE_NOCACHE,%d6
+	movel	#D6F_060+_PAGE_CACHE040W,%d6
 	jra	2f
 1:	btst	#CPUB_68040,%d0
 	jeq	1f
@@ -604,8 +604,10 @@
  * will overlap later virtual location, but as we already mapped the first
  * 16MB to 0x80000000, we can jump there after translation and MMU is enabled
  * and then we can switch off translation and go to the final place.
+ * On 020/030 we must emulate transparant translation, since 020 doesn't know
+ * it, but due to early termination pointer this is easy to do.
  * When MMU is enabled, stack pointer and Lcustom will become again valid and
- * points to the unused first page.
+ * stack points to the unused first page.
  */
 
 /*
@@ -622,15 +624,16 @@
 
 	is_040_or_060(Lamimmu68040)
 
-	lea	2f:w,%a0
+	moveq	#ROOT_INDEX_SHIFT,%d2
 	movel	%d5,%d0
-	andl	#0xff000000,%d0
-	jne	1f
-	lea	%pc@(2f+0x80000000),%a0
-1:	orw	#TTR_ENABLE+TTR_CI+TTR_RWM+TTR_FCB2+TTR_FCM1+TTR_FCM0,%d0
+	lsrl	%d2,%d0
+	movel	%d0,%d1
+	lsll	%d2,%d1
+	addql	#_PAGE_PRESENT,%d1
+	lsll	#2,%d0
+	movel	%a5@(%d0:w),%d2
+	movel	%d1,%a5@(%d0:w)
 	lea	%pc@(Lmmu),%a3
-	movel	%d0,%a3@
-	.long	0xf0130800	/* pmove %a3@,%tt0 */
 	/* no limit, 4byte descriptors */
 	movel	#0x80000002,%a3@
 	movel	%a5,%a3@(4)
@@ -643,9 +646,12 @@
 	 */
 	movel	#0x82c07760,%a3@
 	.long	0xf0134000	/* pmove %a3@,%tc (enable the MMU) */
-	jmp	%a0@
-2:	clrl	%a3@
-	.long	0xf0130800	/* pmove %a3@,%tt0 */
+	tstl	%d0
+	jne	1f
+	jmp	%pc@(2f+0x80000000)
+1:	jmp	2f:w
+2:	movel	%d2,%a5@(%d0:w)
+	.long	0xf0002400	/* pflusha */
 	jmp	LdoneMMUenable:w
 
 Lamimmu68040:
@@ -708,7 +714,7 @@
 	 * Requires that this code until after MMU enabling lies in
 	 * the 256K page around %d5
 	 */
-2:	movel	%a4@,%d1
+2:	movel	%a5@,%d1
 	andw	#0xfff0,%d1
 	movel	%d1,%a1
 	movel	%d5,%d1
@@ -748,7 +754,8 @@
 	andl	#0xff000000,%d0 /* logical address base */
 	orw	#TTR_ENABLE+TTR_KERNELMODE+_PAGE_NOCACHE_S,%d0
 	.long	0x4e7b0004	/* movec %d0,%itt0 */
-2:	.word	0xf518		/* pflusha */
+2:	nop
+	.word	0xf518		/* pflusha */
 	.long	0x4e7bd807	/* movec %a5,%srp */
 	.long	0x4e7bd806	/* movec %a5,%urp */
 	movel	#TC_ENABLE+TC_PAGE4K,%d0
@@ -1096,7 +1103,7 @@
 	putc('=')
 	putn(%a1)
 
-	ptestr	#5,%a1@,#7,%a0
+	.long	0xf0119f15		| ptestr	#5,%a1@,#7,%a0
 
 	putc('D')
 	putc('A')
@@ -1110,7 +1117,7 @@
 	putc('S')
 	putc('=')
 	lea	%pc@(Lmmu),%a0
-	pmove	%psr,%a0@
+	.long	0xf0106200		| pmove		%psr,%a0@
 	clrl	%d7
 	movew	%a0@,%d7
 	jbsr	Lserial_putnum
diff -urN pc-2.0.21/arch/m68k/kernel/ints.c linux-2.0.21/arch/m68k/kernel/ints.c
--- pc-2.0.21/arch/m68k/kernel/ints.c	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/kernel/ints.c	Sat Sep 14 15:17:38 1996
@@ -1,5 +1,5 @@
 /*
- * ints.c -- 680x0 Linux general interrupt handling code
+ * linux/arch/m68k/kernel/ints.c -- Linux/m68k general interrupt handling code
  *
  * 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
@@ -11,6 +11,18 @@
  *           remove_isr() to request_irq() and free_irq()
  *           respectively, so they are compliant with the other
  *           architectures.                                     /Jes
+ * 11/07/96: Changed all add_/remove_isr() to request_/free_irq() calls.
+ *           Removed irq list support, if any machine needs an irq server
+ *           it must implement this itself (as it's already done), instead
+ *           only default handler are used with mach_default_handler.
+ *           request_irq got some flags different from other architectures:
+ *           - IRQ_FLG_REPLACE : Replace an existing handler (the default one
+ *                               can be replaced without this flag)
+ *           - IRQ_FLG_LOCK : handler can't be replaced
+ *           There are other machine depending flags, see there
+ *           If you want to replace a default handler you should know what
+ *           you're doing, since it might handle different other irq sources
+ *           which must be served                               /Roman Zippel
  */
 
 #include <linux/types.h>
@@ -24,14 +36,19 @@
 #include <asm/page.h>
 #include <asm/machdep.h>
 
-/* list is accessed 0-6 for IRQs 1-7 */
-static isr_node_t *isr_list[7];
+/* table for system interrupt handlers */
+static irq_handler_t irq_list[SYS_IRQS];
+
+static const char *default_names[SYS_IRQS] = {
+	"spurious int", "int1 handler", "int2 handler", "int3 handler",
+	"int4 handler", "int5 handler", "int6 handler", "int7 handler"
+};
 
 /* The number of spurious interrupts */
-volatile unsigned long num_spurious;
-/*
-unsigned long interrupt_stack[PAGE_SIZE/sizeof(long)];
-*/
+volatile unsigned int num_spurious;
+
+#define NUM_IRQ_NODES 100
+static irq_node_t nodes[NUM_IRQ_NODES];
 
 /*
  * void init_IRQ(void)
@@ -46,147 +63,85 @@
 
 void init_IRQ(void)
 {
-    /* Setup interrupt stack pointer */
-  /*
-    asm ("movec %0,%/isp"
-	 : : "r" (interrupt_stack + sizeof (interrupt_stack) / sizeof (long)));
-	 */
-    mach_init_INTS ();
-}
-
-void insert_isr (isr_node_t **listp, isr_node_t *node)
-{
-    unsigned long spl;
-    isr_node_t *cur;
-
-    save_flags(spl);
-    cli();
-
-    cur = *listp;
-
-    while (cur && cur->pri <= node->pri)
-    {
-	listp = &cur->next;
-	cur = cur->next;
-    }
-
-    node->next = cur;
-    *listp = node;
-
-    restore_flags(spl);
-}
-
-void delete_isr (isr_node_t **listp, isrfunc isr, void *data)
-{
-    unsigned long flags;
-    isr_node_t *np;
+	int i;
 
-    save_flags(flags);
-    cli();
-    for (np = *listp; np; listp = &np->next, np = *listp) {
-	if (np->isr == isr && np->data == data) {
-	    *listp = np->next;
-	    /* Mark it as free. */
-	    np->isr = NULL;
-	    restore_flags(flags);
-	    return;
+	for (i = 0; i < SYS_IRQS; i++) {
+		if (mach_default_handler)
+			irq_list[i].handler = (*mach_default_handler)[i];
+		irq_list[i].flags   = IRQ_FLG_STD;
+		irq_list[i].dev_id  = NULL;
+		irq_list[i].devname = default_names[i];
 	}
-    }
-    restore_flags(flags);
-    printk ("delete_isr: isr %p not found on list!\n", isr);
-}
 
-#define NUM_ISR_NODES 100
-static isr_node_t nodes[NUM_ISR_NODES];
-
-isr_node_t *new_isr_node(void)
-{
-    isr_node_t *np;
+	for (i = 0; i < NUM_IRQ_NODES; i++)
+		nodes[i].handler = NULL;
 
-    for (np = nodes; np < &nodes[NUM_ISR_NODES]; np++)
-	if (np->isr == NULL)
-	    return np;
-
-    printk ("new_isr_node: out of nodes");
-    return NULL;
+	mach_init_IRQ ();
 }
 
-int add_isr (unsigned long source, isrfunc isr, int pri, void *data,
-	     char *name)
+irq_node_t *new_irq_node(void)
 {
-    isr_node_t *p;
-
-    if (source & IRQ_MACHSPEC)
-    {
-	return mach_add_isr (source, isr, pri, data, name);
-    }
+	irq_node_t *node;
+	short i;
 
-    if (source < IRQ1 || source > IRQ7)
-	panic ("add_isr: Incorrect IRQ source %ld from %s\n", source, name);
+	for (node = nodes, i = NUM_IRQ_NODES-1; i >= 0; node++, i--)
+		if (!node->handler)
+			return node;
 
-    p = new_isr_node();
-    if (p == NULL)
-	return 0;
-    p->isr = isr;
-    p->pri = pri;
-    p->data = data;
-    p->name = name;
-    p->next = NULL;
-
-    insert_isr (&isr_list[source-1], p);
-
-    return 1;
+	printk ("new_irq_node: out of nodes\n");
+	return NULL;
 }
 
-int remove_isr (unsigned long source, isrfunc isr, void *data)
+int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
+                unsigned long flags, const char *devname, void *dev_id)
 {
-    if (source & IRQ_MACHSPEC)
-	return mach_remove_isr (source, isr, data);
-
-    if (source < IRQ1 || source > IRQ7) {
-	printk ("remove_isr: Incorrect IRQ source %ld\n", source);
-	return 0;
-    }
+	if (irq & IRQ_MACHSPEC)
+		return mach_request_irq(IRQ_IDX(irq), handler, flags, devname, dev_id);
 
-    delete_isr (&isr_list[source - 1], isr, data);
-    return 1;
-}
+	if (irq < IRQ1 || irq > IRQ7) {
+		printk("%s: Incorrect IRQ %d from %s\n", __FUNCTION__, irq, devname);
+		return -ENXIO;
+	}
 
-void call_isr_list(int irq, isr_node_t *p, struct pt_regs *fp)
-{
-    while (p) {
-	p->isr (irq, fp, p->data);
-	p = p->next;
-    }
+	if (!(irq_list[irq].flags & IRQ_FLG_STD)) {
+		if (irq_list[irq].flags & IRQ_FLG_LOCK) {
+			printk("%s: IRQ %d from %s is not replaceable\n",
+			       __FUNCTION__, irq, irq_list[irq].devname);
+			return -EBUSY;
+		}
+		if (flags & IRQ_FLG_REPLACE) {
+			printk("%s: %s can't replace IRQ %d from %s\n",
+			       __FUNCTION__, devname, irq, irq_list[irq].devname);
+			return -EBUSY;
+		}
+	}
+	irq_list[irq].handler = handler;
+	irq_list[irq].flags   = flags;
+	irq_list[irq].dev_id  = dev_id;
+	irq_list[irq].devname = devname;
+	return 0;
 }
 
-asmlinkage void process_int(int vec, struct pt_regs *regs)
+void free_irq(unsigned int irq, void *dev_id)
 {
-	int level;
-
-	if (vec >= VECOFF(VEC_INT1) && vec <= VECOFF(VEC_INT7))
-		level = (vec - VECOFF(VEC_SPUR)) >> 2;
-	else {
-		if (mach_process_int)
-			mach_process_int(vec, regs);
-		else
-			panic("Can't process interrupt vector 0x%03x\n", vec);
+	if (irq & IRQ_MACHSPEC) {
+		mach_free_irq(IRQ_IDX(irq), dev_id);
 		return;
 	}
 
-	kstat.interrupts[level]++;  	
-	call_isr_list (level, isr_list[level-1], regs);
-}
-
-int request_irq(unsigned int irq,
-		void (*handler)(int, void *, struct pt_regs *),
-		unsigned long flags, const char * devname, void *dev_id)
-{
-	return -EINVAL;
-}
+	if (irq < IRQ1 || irq > IRQ7) {
+		printk("%s: Incorrect IRQ %d\n", __FUNCTION__, irq);
+		return;
+	}
 
-void free_irq(unsigned int irq, void *dev_id)
-{
+	if (irq_list[irq].dev_id != dev_id)
+		printk("%s: Removing probably wrong IRQ %d from %s\n",
+		       __FUNCTION__, irq, irq_list[irq].devname);
+
+	irq_list[irq].handler = (*mach_default_handler)[irq];
+	irq_list[irq].flags   = IRQ_FLG_STD;
+	irq_list[irq].dev_id  = NULL;
+	irq_list[irq].devname = default_names[irq];
 }
 
 /*
@@ -194,43 +149,58 @@
  */
 unsigned long probe_irq_on (void)
 {
-  return 0;
+	return 0;
 }
 
 int probe_irq_off (unsigned long irqs)
 {
-  return 0;
+	return 0;
 }
 
-void enable_irq(unsigned int irq_nr)
+void enable_irq(unsigned int irq)
 {
-	if ((irq_nr & IRQ_MACHSPEC) && mach_enable_irq)
-		mach_enable_irq(irq_nr);
+	if ((irq & IRQ_MACHSPEC) && mach_enable_irq)
+		mach_enable_irq(IRQ_IDX(irq));
 }
 
-void disable_irq(unsigned int irq_nr)
+void disable_irq(unsigned int irq)
 {
-	if ((irq_nr & IRQ_MACHSPEC) && mach_disable_irq)
-		mach_disable_irq(irq_nr);
+	if ((irq & IRQ_MACHSPEC) && mach_disable_irq)
+		mach_disable_irq(IRQ_IDX(irq));
+}
+
+asmlinkage void process_int(unsigned long vec, struct pt_regs *fp)
+{
+	if (vec < VEC_INT1 || vec > VEC_INT7) {
+		if (mach_process_int)
+			mach_process_int(vec, fp);
+		else
+			panic("Can't process interrupt vector %ld\n", vec);
+		return;
+	}
+
+	vec -= VEC_SPUR;
+	kstat.interrupts[vec]++;
+	irq_list[vec].handler(vec, irq_list[vec].dev_id, fp);
 }
 
 int get_irq_list(char *buf)
 {
-    int i, len = 0;
-    isr_node_t *p;
-    
-    /* autovector interrupts */
-    for (i = IRQ1; i <= IRQ7; ++i) {
-	if (!isr_list[i-1])
-	    continue;
-	len += sprintf(buf+len, "auto %2d: %8d ", i, kstat.interrupts[i]);
-	for (p = isr_list[i-1]; p; p = p->next) {
-	    len += sprintf(buf+len, "%s\n", p->name);
-	    if (p->next)
-		len += sprintf(buf+len, "                  ");
+	int i, len = 0;
+
+	/* autovector interrupts */
+	if (mach_default_handler) {
+		for (i = 0; i < SYS_IRQS; i++) {
+			len += sprintf(buf+len, "auto %2d: %10u ", i,
+			               i ? kstat.interrupts[i] : num_spurious);
+			if (irq_list[i].flags & IRQ_FLG_LOCK)
+				len += sprintf(buf+len, "L ");
+			else
+				len += sprintf(buf+len, "  ");
+			len += sprintf(buf+len, "%s\n", irq_list[i].devname);
+		}
 	}
-    }
 
-    len = mach_get_irq_list(buf, len);
-    return len;
+	len += mach_get_irq_list(buf+len);
+	return len;
 }
diff -urN pc-2.0.21/arch/m68k/kernel/ksyms.c linux-2.0.21/arch/m68k/kernel/ksyms.c
--- pc-2.0.21/arch/m68k/kernel/ksyms.c	Thu May 16 08:05:10 1996
+++ linux-2.0.21/arch/m68k/kernel/ksyms.c	Wed Sep 18 19:04:27 1996
@@ -1,14 +1,16 @@
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/linkage.h>
+#include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/mm.h>
 #include <linux/user.h>
 #include <linux/elfcore.h>
 
-#include <asm/bootinfo.h>
+#include <asm/setup.h>
 #include <asm/pgtable.h>
 #include <asm/irq.h>
+#include <asm/semaphore.h>
 
 asmlinkage long long __ashrdi3 (long long, int);
 extern char m68k_debug_device[];
@@ -34,14 +36,16 @@
 	X(boot_info),
 	X(m68k_is040or060),
 	X(cache_push),
+	X(cache_push_v),
 	X(cache_clear),
 	X(mm_vtop),
 	X(mm_ptov),
 	X(m68k_debug_device),
-	X(add_isr),
-	X(remove_isr),
+	X(request_irq),
+	X(free_irq),
 	X(dump_fpu),
 	X(dump_thread),
+	X(strnlen),
 
 	/* The following are special because they're not called
 	   explicitly (the C compiler generates them).  Fortunately,
@@ -49,7 +53,11 @@
 	   it's OK to leave it out of version control.  */
 	XNOVERS(__ashrdi3),
 	XNOVERS(memcpy),
-	
+	XNOVERS(memset),
+
+	XNOVERS(down_failed),
+	XNOVERS(up_wakeup),
+
 #include <linux/symtab_end.h>
 };
 
diff -urN pc-2.0.21/arch/m68k/kernel/process.c linux-2.0.21/arch/m68k/kernel/process.c
--- pc-2.0.21/arch/m68k/kernel/process.c	Mon Apr  1 20:40:27 1996
+++ linux-2.0.21/arch/m68k/kernel/process.c	Mon Aug 26 23:55:14 1996
@@ -2,6 +2,8 @@
  *  linux/arch/m68k/kernel/process.c
  *
  *  Copyright (C) 1995  Hamish Macdonald
+ *
+ *  68060 fixes by Jesper Skov
  */
 
 /*
@@ -23,6 +25,7 @@
 #include <asm/system.h>
 #include <asm/traps.h>
 #include <asm/machdep.h>
+#include <asm/setup.h>
 
 asmlinkage void ret_from_exception(void);
 
@@ -125,10 +128,17 @@
 
 	p->tss.usp = usp;
 	p->tss.ksp = (unsigned long)childstack;
+	/*
+	 * Must save the current SFC/DFC value, NOT the value when
+	 * the parent was last descheduled - RGH  10-08-96
+	 */
+	p->tss.fs = get_fs();
 
 	/* Copy the current fpu state */
 	asm volatile ("fsave %0" : : "m" (p->tss.fpstate[0]) : "memory");
-	if (p->tss.fpstate[0])
+
+	if((!CPU_IS_060 && p->tss.fpstate[0]) ||
+	   (CPU_IS_060 && p->tss.fpstate[2]))
 	  asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t"
 			"fmoveml %/fpiar/%/fpcr/%/fpsr,%1"
 			: : "m" (p->tss.fp[0]), "m" (p->tss.fpcntl[0])
@@ -145,8 +155,8 @@
 
   /* First dump the fpu context to avoid protocol violation.  */
   asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory");
-  if (!fpustate[0])
-    return 0;
+  if((!CPU_IS_060 && !fpustate[0]) || (CPU_IS_060 && !fpustate[2]))
+     return 0;
 
   asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0"
 		:: "m" (fpu->fpcntl[0])
diff -urN pc-2.0.21/arch/m68k/kernel/ptrace.c linux-2.0.21/arch/m68k/kernel/ptrace.c
--- pc-2.0.21/arch/m68k/kernel/ptrace.c	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/kernel/ptrace.c	Thu Sep 12 21:29:42 1996
@@ -198,6 +198,7 @@
 /* this is a hack for non-kernel-mapped video buffers and similar */
 	if (page < high_memory) {
 		*(unsigned long *) (page + (addr & ~PAGE_MASK)) = data;
+		flush_page_to_ram (page);
 	}
 /* we're bypassing pagetables, so we have to set the dirty bit ourselves */
 /* this should also re-instate whatever read-only mode there was before */
@@ -210,7 +211,7 @@
 	struct vm_area_struct * vma;
 
 	addr &= PAGE_MASK;
-	vma = find_vma(tsk,addr);
+	vma = find_vma(tsk->mm, addr);
 	if (!vma)
 		return NULL;
 	if (vma->vm_start <= addr)
diff -urN pc-2.0.21/arch/m68k/kernel/setup.c linux-2.0.21/arch/m68k/kernel/setup.c
--- pc-2.0.21/arch/m68k/kernel/setup.c	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/kernel/setup.c	Sat Aug 24 14:30:55 1996
@@ -19,7 +19,7 @@
 #include <linux/errno.h>
 #include <linux/string.h>
 
-#include <asm/bootinfo.h>
+#include <asm/setup.h>
 #include <asm/irq.h>
 #include <asm/machdep.h>
 #include <asm/amigatypes.h>
@@ -47,17 +47,22 @@
 {
 }
 
-void (*mach_sched_init) (isrfunc);
+void (*mach_sched_init) (void (*handler)(int, void *, struct pt_regs *));
+/* machine dependent keyboard functions */
 int (*mach_keyb_init) (void);
 int (*mach_kbdrate) (struct kbd_repeat *) = NULL;
 void (*mach_kbd_leds) (unsigned int) = NULL;
-void (*mach_init_INTS) (void);
-int (*mach_add_isr) (unsigned long, isrfunc, int, void *, char *);
-int (*mach_remove_isr) (unsigned long, isrfunc, void *);
+/* machine dependent irq functions */
+void (*mach_init_IRQ) (void);
+void (*(*mach_default_handler)[]) (int, void *, struct pt_regs *) = NULL;
+int (*mach_request_irq) (unsigned int, void (*)(int, void *, struct pt_regs *),
+                         unsigned long, const char *, void *);
+int (*mach_free_irq) (unsigned int, void *);
+void (*mach_enable_irq) (unsigned int) = NULL;
+void (*mach_disable_irq) (unsigned int) = NULL;
+int (*mach_get_irq_list) (char *) = NULL;
 void (*mach_process_int) (int, struct pt_regs *) = NULL;
-void (*mach_enable_irq) (unsigned) = NULL;
-void (*mach_disable_irq) (unsigned) = NULL;
-int (*mach_get_irq_list) (char *, int) = NULL;
+/* machine dependent timer functions */
 unsigned long (*mach_gettimeoffset) (void);
 void (*mach_gettod) (int*, int*, int*, int*, int*, int*);
 int (*mach_hwclk) (int, struct hwclk_time*) = NULL;
@@ -78,6 +83,7 @@
 extern void config_amiga(void);
 extern void config_atari(void);
 extern void config_mac(void);
+extern void config_sun3(void);
 
 extern void register_console(void (*proc)(const char *));
 extern void ami_serial_print (const char *str);
@@ -102,18 +108,15 @@
 	int i;
 	char *p, *q;
 
-#ifdef CONFIG_AMIGA
 	if (MACH_IS_AMIGA)
-	  register_console (ami_serial_print);
-#endif
-#ifdef CONFIG_ATARI
+		register_console(ami_serial_print);
+
 	if (MACH_IS_ATARI)
-	  register_console (ata_serial_print);
-#endif
+		register_console(ata_serial_print);
 
-	if (boot_info.cputype & CPU_68040)
+	if (CPU_IS_040)
 		m68k_is040or060 = 4;
-	else if (boot_info.cputype & CPU_68060)
+	else if (CPU_IS_060)
 		m68k_is040or060 = 6;
 
 	/* clear the fpu if we have one */
@@ -179,6 +182,11 @@
 		config_mac();
 		break;
 #endif
+#ifdef CONFIG_SUN3
+	    case MACH_SUN3:
+	    	config_sun3();
+	    	break;
+#endif
 	    default:
 		panic ("No configuration setup");
 	}
@@ -191,39 +199,29 @@
 #endif
 }
 
-int setkeycode(unsigned int scancode, unsigned int keycode)
-{
-	return -EOPNOTSUPP;
-}
-
-int getkeycode(unsigned int scancode)
-{
-	return -EOPNOTSUPP;
-}
-
 int get_cpuinfo(char * buffer)
 {
     char *cpu, *mmu, *fpu;
     u_long clockfreq, clockfactor;
 
-#define CLOCK_FACTOR_68020	(8046)	/*  3107016 loops/s @ 25 MHz (Sun-3) */
-#define CLOCK_FACTOR_68030	(8010)	/*  3994575 loops/s @ 32 MHz */
-#define CLOCK_FACTOR_68040	(3010)	/*  8305552 loops/s @ 25 MHz */
-#define CLOCK_FACTOR_68060	(998)	/* 50081241 loops/s @ 50 MHz */
+#define LOOP_CYCLES_68020	(8)
+#define LOOP_CYCLES_68030	(8)
+#define LOOP_CYCLES_68040	(3)
+#define LOOP_CYCLES_68060	(1)
 
-    if (boot_info.cputype & CPU_68020) {
+    if (CPU_IS_020) {
 	cpu = "68020";
 	mmu = "68851";
-	clockfactor = CLOCK_FACTOR_68020;
-    } else if (boot_info.cputype & CPU_68030) {
+	clockfactor = LOOP_CYCLES_68020;
+    } else if (CPU_IS_030) {
 	cpu = mmu = "68030";
-	clockfactor = CLOCK_FACTOR_68030;
-    } else if (boot_info.cputype & CPU_68040) {
+	clockfactor = LOOP_CYCLES_68030;
+    } else if (CPU_IS_040) {
 	cpu = mmu = "68040";
-	clockfactor = CLOCK_FACTOR_68040;
-    } else if (boot_info.cputype & CPU_68060) {
+	clockfactor = LOOP_CYCLES_68040;
+    } else if (CPU_IS_060) {
 	cpu = mmu = "68060";
-	clockfactor = CLOCK_FACTOR_68060;
+	clockfactor = LOOP_CYCLES_68060;
     } else {
 	cpu = mmu = "680x0";
 	clockfactor = 0;
@@ -240,16 +238,19 @@
     else
 	fpu = "none";
 
-    clockfreq = loops_per_sec/1000*clockfactor;
+    clockfreq = loops_per_sec*clockfactor;
 
     return(sprintf(buffer, "CPU:\t\t%s\n"
 		   "MMU:\t\t%s\n"
 		   "FPU:\t\t%s\n"
-		   "Clockspeed:\t%lu.%1luMHz\n"
-		   "BogoMips:\t%lu.%02lu\n",
-		   cpu, mmu, fpu, (clockfreq+50000)/1000000,
-		   ((clockfreq+50000)/100000)%10, loops_per_sec/500000,
-		   (loops_per_sec/5000)%100));
+		   "Clocking:\t%lu.%1luMHz\n"
+		   "BogoMips:\t%lu.%02lu\n"
+		   "Calibration:\t%lu loops\n",
+		   cpu, mmu, fpu,
+		   clockfreq/1000000,(clockfreq/100000)%10,
+		   loops_per_sec/500000,(loops_per_sec/5000)%100,
+		   loops_per_sec));
+
 }
 
 int get_hardware_list(char *buffer)
diff -urN pc-2.0.21/arch/m68k/kernel/signal.c linux-2.0.21/arch/m68k/kernel/signal.c
--- pc-2.0.21/arch/m68k/kernel/signal.c	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/kernel/signal.c	Tue Sep 17 23:28:54 1996
@@ -9,7 +9,17 @@
  */
 
 /*
- * 680x0 support by Hamish Macdonald
+ * Linux/m68k support by Hamish Macdonald
+ *
+ * 68060 fixes by Jesper Skov
+ */
+
+/*
+ * ++roman (07/09/96): implemented signal stacks (specially for tosemu on
+ * Atari :-) Current limitation: Only one sigstack can be active at one time.
+ * If a second signal with SA_STACK set arrives while working on a sigstack,
+ * SA_STACK is ignored. This behaviour avoids lots of trouble with nested
+ * signal handlers!
  */
 
 #include <linux/sched.h>
@@ -21,10 +31,10 @@
 #include <linux/ptrace.h>
 #include <linux/unistd.h>
 
+#include <asm/setup.h>
 #include <asm/segment.h>
 #include <asm/pgtable.h>
 #include <asm/traps.h>
-#include <asm/bootinfo.h>
 
 #define offsetof(type, member)  ((size_t)(&((type *)0)->member))
 
@@ -74,13 +84,10 @@
 
 static unsigned char fpu_version = 0;	/* version number of fpu, set by setup_frame */
 
-/*
- * This sets regs->usp even though we don't actually use sigstacks yet..
- */
 asmlinkage int do_sigreturn(unsigned long __unused)
 {
 	struct sigcontext_struct context;
-	struct frame * regs;
+	struct pt_regs *regs;
 	struct switch_stack *sw;
 	int fsize = 0;
 	int formatvec = 0;
@@ -93,7 +100,7 @@
 
 	/* get stack frame pointer */
 	sw = (struct switch_stack *) &__unused;
-	regs = (struct frame *) (sw + 1);
+	regs = (struct pt_regs *) (sw + 1);
 
 	/* get previous context (including pointer to possible extra junk) */
         if (verify_area(VERIFY_READ, (void *)usp, sizeof(context)))
@@ -107,37 +114,21 @@
 	current->blocked = context.sc_mask & _BLOCKABLE;
 
 	/* restore passed registers */
-	regs->ptregs.d0 = context.sc_d0;
-	regs->ptregs.d1 = context.sc_d1;
-	regs->ptregs.a0 = context.sc_a0;
-	regs->ptregs.a1 = context.sc_a1;
-	regs->ptregs.sr = (regs->ptregs.sr & 0xff00)|(context.sc_sr & 0xff);
-	regs->ptregs.pc = context.sc_pc;
-
+	regs->d0 = context.sc_d0;
+	regs->d1 = context.sc_d1;
+	regs->a0 = context.sc_a0;
+	regs->a1 = context.sc_a1;
+	regs->sr = (regs->sr & 0xff00) | (context.sc_sr & 0xff);
+	regs->pc = context.sc_pc;
+	regs->orig_d0 = -1;		/* disable syscall checks */
 	wrusp(context.sc_usp);
 	formatvec = context.sc_formatvec;
-	regs->ptregs.format = formatvec >> 12;
-	regs->ptregs.vector = formatvec & 0xfff;
-	if (context.sc_fpstate[0])
-	  {
+	regs->format = formatvec >> 12;
+	regs->vector = formatvec & 0xfff;
+	if ((!CPU_IS_060 && context.sc_fpstate[0]) || (CPU_IS_060 && context.sc_fpstate[2])){
 	    /* Verify the frame format.  */
-	    if (context.sc_fpstate[0] != fpu_version){
-#if DEBUG
-	    printk("fpregs=%08x fpcntl=%08x\n", context.sc_fpregs,
-		   context.sc_fpcntl); 
-	    printk("Wrong fpu: sc_fpstate[0]=%02x fpu_version=%02x\n",
-		   (unsigned) context.sc_fpstate[0], (unsigned) fpu_version);
-	    {
-	      int i;
-	      printk("Saved fp_state: ");
-	      for (i = 0; i < 216; i++){
-		printk("%02x ", context.sc_fpstate[i]);
-	      }
-	      printk("\n");
-	    }
-#endif
+	    if (!CPU_IS_060 && (context.sc_fpstate[0] != fpu_version))
 	      goto badframe;
-	    }
 	    if (boot_info.cputype & FPU_68881)
 	      {
 		if (context.sc_fpstate[1] != 0x18
@@ -147,34 +138,22 @@
 	    else if (boot_info.cputype & FPU_68882)
 	      {
 		if (context.sc_fpstate[1] != 0x38
-		    && context.sc_fpstate[1] != 0xd4){
-#if 0
-		  printk("Wrong 68882 fpu-state\n");
-#endif
+		    && context.sc_fpstate[1] != 0xd4)
 		  goto badframe;
-		}
 	      }
 	    else if (boot_info.cputype & FPU_68040)
 	      {
-		if (!((context.sc_fpstate[1] == 0x00)|| \
-                      (context.sc_fpstate[1] == 0x28)|| \
-                      (context.sc_fpstate[1] == 0x60))){
-#if 0
-		  printk("Wrong 68040 fpu-state\n");
-#endif
+		if (!(context.sc_fpstate[1] == 0x00 ||
+                      context.sc_fpstate[1] == 0x28 ||
+                      context.sc_fpstate[1] == 0x60))
 		  goto badframe;
-		}
 	      }
 	    else if (boot_info.cputype & FPU_68060)
 	      {
-		if (!((context.sc_fpstate[1] == 0x00)|| \
-                      (context.sc_fpstate[1] == 0x60)|| \
-                      (context.sc_fpstate[1] == 0xe0))){
-#if 0
-		  printk("Wrong 68060 fpu-state\n");
-#endif
+		if (!(context.sc_fpstate[3] == 0x00 ||
+                      context.sc_fpstate[3] == 0x60 ||
+		      context.sc_fpstate[3] == 0xe0))
 		  goto badframe;
-		}
 	      }
 	    __asm__ volatile ("fmovemx %0,%/fp0-%/fp1\n\t"
 			      "fmoveml %1,%/fpcr/%/fpsr/%/fpiar"
@@ -184,17 +163,20 @@
 	  }
 	__asm__ volatile ("frestore %0" : : "m" (*context.sc_fpstate));
 
-	fsize = extra_sizes[regs->ptregs.format];
+	fsize = extra_sizes[regs->format];
 	if (fsize < 0) {
 		/*
 		 * user process trying to return with weird frame format
 		 */
 #if DEBUG
-	      printk("user process returning with weird frame format\n");
+		printk("user process returning with weird frame format\n");
 #endif
 		goto badframe;
 	}
 
+	if (context.sc_usp != fp+fsize)
+		current->flags &= ~PF_ONSIGSTK;
+	
 	/* OK.	Make room on the supervisor stack for the extra junk,
 	 * if necessary.
 	 */
@@ -227,7 +209,7 @@
 		/* NOTREACHED */
 	}
 
-	return regs->ptregs.d0;
+	return regs->d0;
 badframe:
         do_exit(SIGSEGV);
 }
@@ -275,25 +257,31 @@
 
 #define UFRAME_SIZE(fs) (sizeof(struct sigcontext_struct)/4 + 6 + fs/4)
 
-static void setup_frame (struct sigaction * sa, unsigned long **fp,
-			 unsigned long pc, struct frame *regs, int
-			 signr, unsigned long oldmask)
+static void setup_frame (struct sigaction * sa, struct pt_regs *regs,
+			 int signr, unsigned long oldmask)
 {
 	struct sigcontext_struct context;
 	unsigned long *frame, *tframe;
-	int fsize = extra_sizes[regs->ptregs.format];
+	int fsize = extra_sizes[regs->format];
 
 	if (fsize < 0) {
 		printk ("setup_frame: Unknown frame format %#x\n",
-			regs->ptregs.format);
+			regs->format);
 		do_exit(SIGSEGV);
 	}
-	frame = *fp - UFRAME_SIZE(fsize);
+
+	frame = (unsigned long *)rdusp();
+	if (!(current->flags & PF_ONSIGSTK) && (sa->sa_flags & SA_STACK)) {
+		frame = (unsigned long *)sa->sa_restorer;
+		current->flags |= PF_ONSIGSTK;
+	}
+	frame -= UFRAME_SIZE(fsize);
+
 	if (verify_area(VERIFY_WRITE,frame,UFRAME_SIZE(fsize)*4))
 		do_exit(SIGSEGV);
 	if (fsize) {
-		memcpy_tofs (frame + UFRAME_SIZE(0), &regs->un, fsize);
-		regs->ptregs.stkadj = fsize;
+		memcpy_tofs (frame + UFRAME_SIZE(0), regs + 1, fsize);
+		regs->stkadj = fsize;
 	}
 
 /* set up the "normal" stack seen by the signal handler */
@@ -307,7 +295,7 @@
 	    put_user(signr, tframe);
 	tframe++;
 
-	put_user(regs->ptregs.vector, tframe); tframe++;
+	put_user(regs->vector, tframe); tframe++;
 	/* "scp" parameter.  points to sigcontext */
 	put_user((ulong)(frame+6), tframe); tframe++;
 
@@ -320,60 +308,85 @@
 
 /* setup and copy the sigcontext structure */
 	context.sc_mask       = oldmask;
-	context.sc_usp	      = (unsigned long)*fp;
-	context.sc_d0	      = regs->ptregs.d0;
-	context.sc_d1	      = regs->ptregs.d1;
-	context.sc_a0	      = regs->ptregs.a0;
-	context.sc_a1	      = regs->ptregs.a1;
-	context.sc_sr	      = regs->ptregs.sr;
-	context.sc_pc	      = pc;
-	context.sc_formatvec  = (regs->ptregs.format << 12 |
-				 regs->ptregs.vector);
-#if DEBUG
-	printk("formatvec: %02x\n", (unsigned) context.sc_formatvec);
-#endif
+	context.sc_usp	      = rdusp();
+	context.sc_d0	      = regs->d0;
+	context.sc_d1	      = regs->d1;
+	context.sc_a0	      = regs->a0;
+	context.sc_a1	      = regs->a1;
+	context.sc_sr	      = regs->sr;
+	context.sc_pc	      = regs->pc;
+	context.sc_formatvec  = (regs->format << 12 | regs->vector);
 	__asm__ volatile ("fsave %0" : : "m" (*context.sc_fpstate) : "memory");
-	if (context.sc_fpstate[0])
-	  {
-	    fpu_version = context.sc_fpstate[0];
-#if DEBUG
-	    {
-	      int i;
-	      printk("Saved fp_state: ");
-	      for (i = 0; i < 216; i++){
-		printk("%02x ", context.sc_fpstate[i]);
-	      }
-	      printk("\n");
-	    }
-	    printk("fpregs=%08x fpcntl=%08x\n", context.sc_fpregs,
-		   context.sc_fpcntl); 
-#endif
-	    __asm__ volatile ("fmovemx %/fp0-%/fp1,%0\n\t"
-			      "fmoveml %/fpcr/%/fpsr/%/fpiar,%1"
-			      : /* no outputs */
-			      : "m" (*context.sc_fpregs),
-				"m" (*context.sc_fpcntl)
-			      : "memory");
-	  }
-#if DEBUG
-	{
-	  int i;
-	  printk("Saved fp_state: ");
-	  for (i = 0; i < 216; i++){
-	    printk("%02x ", context.sc_fpstate[i]);
-	  }
-	  printk("\n");
+	if ((!CPU_IS_060 && context.sc_fpstate[0]) || (CPU_IS_060 && context.sc_fpstate[2])){
+		fpu_version = context.sc_fpstate[0];
+		__asm__ volatile ("fmovemx %/fp0-%/fp1,%0\n\t"
+				  "fmoveml %/fpcr/%/fpsr/%/fpiar,%1"
+				  : /* no outputs */
+				  : "m" (*context.sc_fpregs),
+				  "m" (*context.sc_fpcntl)
+				  : "memory");
 	}
-#endif
 	memcpy_tofs (tframe, &context, sizeof(context));
+
 	/*
 	 * no matter what frame format we were using before, we
 	 * will do the "RTE" using a normal 4 word frame.
 	 */
-	regs->ptregs.format = 0;
+	regs->format = 0;
+
+	/* Set up registers for signal handler */
+	wrusp ((unsigned long) frame);
+	regs->pc = (unsigned long) sa->sa_handler;
+
+	/* Prepare to skip over the extra stuff in the exception frame.  */
+	if (regs->stkadj) {
+		struct pt_regs *tregs =
+			(struct pt_regs *)((ulong)regs + regs->stkadj);
+#if DEBUG
+		printk("Performing stackadjust=%04x\n", regs->stkadj);
+#endif
+		/* This must be copied with decreasing addresses to
+                   handle overlaps.  */
+		tregs->vector = regs->vector;
+		tregs->format = regs->format;
+		tregs->pc = regs->pc;
+		tregs->sr = regs->sr;
+	}
+}
 
-	/* "return" new usp to caller */
-	*fp = frame;
+/*
+ * OK, we're invoking a handler
+ */	
+static void handle_signal(unsigned long signr, struct sigaction *sa,
+			  unsigned long oldmask, struct pt_regs *regs)
+{
+	/* are we from a system call? */
+	if (regs->orig_d0 >= 0) {
+		/* If so, check system call restarting.. */
+		switch (regs->d0) {
+			case -ERESTARTNOHAND:
+				regs->d0 = -EINTR;
+				break;
+
+			case -ERESTARTSYS:
+				if (!(sa->sa_flags & SA_RESTART)) {
+					regs->d0 = -EINTR;
+					break;
+				}
+			/* fallthrough */
+			case -ERESTARTNOINTR:
+				regs->d0 = regs->orig_d0;
+				regs->pc -= 2;
+		}
+	}
+
+	/* set up the stack frame */
+	setup_frame(sa, regs, signr, oldmask);
+
+	if (sa->sa_flags & SA_ONESHOT)
+		sa->sa_handler = NULL;
+	if (!(sa->sa_flags & SA_NOMASK))
+		current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE;
 }
 
 /*
@@ -385,52 +398,59 @@
  * that the kernel can handle, and then we build all the user-level signal
  * handling stack-frames in one go after that.
  */
-asmlinkage int do_signal(unsigned long oldmask, struct pt_regs *regs_in)
+asmlinkage int do_signal(unsigned long oldmask, struct pt_regs *regs)
 {
 	unsigned long mask = ~current->blocked;
-	unsigned long handler_signal = 0;
-	unsigned long *frame = NULL;
-	unsigned long pc = 0;
 	unsigned long signr;
-	struct frame *regs = (struct frame *)regs_in;
 	struct sigaction * sa;
 
 	current->tss.esp0 = (unsigned long) regs;
 
+	/* If the process is traced, all signals are passed to the debugger. */
+	if (current->flags & PF_PTRACED)
+		mask = ~0UL;
 	while ((signr = current->signal & mask)) {
 		__asm__("bfffo  %2,#0,#0,%1\n\t"
 			"bfclr  %0,%1,#1\n\t"
 			"eorw   #31,%1"
-			:"=m" (current->signal),"=r" (signr)
-			:"1" (signr));
+			:"=m" (current->signal),"=d" (signr)
+			:"0" (current->signal), "1" (signr));
 		sa = current->sig->action + signr;
 		signr++;
 
 		if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
 			current->exit_code = signr;
 			current->state = TASK_STOPPED;
+
+			/* Did we come from a system call? */
+			if (regs->orig_d0 >= 0) {
+				/* Restart the system call */
+				if (regs->d0 == -ERESTARTNOHAND ||
+				    regs->d0 == -ERESTARTSYS ||
+				    regs->d0 == -ERESTARTNOINTR) {
+					regs->d0 = regs->orig_d0;
+					regs->pc -= 2;
+				}
+			}
 			notify_parent(current);
 			schedule();
 			if (!(signr = current->exit_code)) {
 			discard_frame:
-				/* Make sure that a faulted bus cycle
-				   isn't restarted.  */
-				switch (regs->ptregs.format) {
-				case 7:
-				case 9:
-				case 10:
-				case 11:
-				  regs->ptregs.stkadj = extra_sizes[regs->ptregs.format];
-				  regs->ptregs.format = 0;
-				  break;
-				}
-				continue;
+			    /* Make sure that a faulted bus cycle
+			       isn't restarted (only needed on the
+			       68030).  */
+			    if (regs->format == 10 || regs->format == 11) {
+				regs->stkadj = extra_sizes[regs->format];
+				regs->format = 0;
+			    }
+			    continue;
 			}
 			current->exit_code = 0;
 			if (signr == SIGSTOP)
 				goto discard_frame;
 			if (_S(signr) & current->blocked) {
 				current->signal |= _S(signr);
+				mask &= ~_S(signr);
 				continue;
 			}
 			sa = current->sig->action + signr - 1;
@@ -447,10 +467,13 @@
 			if (current->pid == 1)
 				continue;
 			switch (signr) {
-			    case SIGCONT: case SIGCHLD: case SIGWINCH:
+			case SIGCONT: case SIGCHLD: case SIGWINCH:
 				continue;
 
-			    case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU:
+			case SIGTSTP: case SIGTTIN: case SIGTTOU:
+				if (is_orphaned_pgrp(current->pgrp))
+					continue;
+			case SIGSTOP:
 				if (current->flags & PF_PTRACED)
 					continue;
 				current->state = TASK_STOPPED;
@@ -461,97 +484,46 @@
 				schedule();
 				continue;
 
-			    case SIGQUIT: case SIGILL: case SIGTRAP:
-			    case SIGIOT: case SIGFPE: case SIGSEGV:
+			case SIGQUIT: case SIGILL: case SIGTRAP:
+			case SIGIOT: case SIGFPE: case SIGSEGV:
 				if (current->binfmt && current->binfmt->core_dump) {
-				    if (current->binfmt->core_dump(signr, (struct pt_regs *)regs))
+				    if (current->binfmt->core_dump(signr, regs))
 					signr |= 0x80;
 				}
 				/* fall through */
-			    default:
+			default:
 				current->signal |= _S(signr & 0x7f);
+				current->flags |= PF_SIGNALED;
 				do_exit(signr);
 			}
 		}
-		/*
-		 * OK, we're invoking a handler
-		 */
-		if (regs->ptregs.orig_d0 >= 0) {
-		  if (regs->ptregs.d0 == -ERESTARTNOHAND ||
-		      (regs->ptregs.d0 == -ERESTARTSYS &&
-		       !(sa->sa_flags & SA_RESTART)))
-		    regs->ptregs.d0 = -EINTR;
-		}
-		handler_signal |= 1 << (signr-1);
-		mask &= ~sa->sa_mask;
+		handle_signal(signr, sa, oldmask, regs);
+		return 1;
 	}
-	if (regs->ptregs.orig_d0 >= 0 &&
-	    (regs->ptregs.d0 == -ERESTARTNOHAND ||
-	     regs->ptregs.d0 == -ERESTARTSYS ||
-	     regs->ptregs.d0 == -ERESTARTNOINTR)) {
-		regs->ptregs.d0 = regs->ptregs.orig_d0;
-		regs->ptregs.pc -= 2;
-	}
-	if (!handler_signal)    /* no handler will be called - return 0 */
-	  {
-	    /* If we are about to discard some frame stuff we must
-	       copy over the remaining frame. */
-	    if (regs->ptregs.stkadj)
-	      {
-		struct frame *tregs =
-		  (struct frame *) ((ulong) regs + regs->ptregs.stkadj);
 
-		/* This must be copied with decreasing addresses to
-		   handle overlaps.  */
-		tregs->ptregs.vector = regs->ptregs.vector;
-		tregs->ptregs.format = regs->ptregs.format;
-		tregs->ptregs.pc = regs->ptregs.pc;
-		tregs->ptregs.sr = regs->ptregs.sr;
-	      }
-	    return 0;
-	  }
-	pc = regs->ptregs.pc;
-	frame = (unsigned long *)rdusp();
-	signr = 1;
-	sa = current->sig->action;
-	for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
-		if (mask > handler_signal)
-			break;
-		if (!(mask & handler_signal))
-			continue;
-		setup_frame(sa,&frame,pc,regs,signr,oldmask);
-		pc = (unsigned long) sa->sa_handler;
-		if (sa->sa_flags & SA_ONESHOT)
-			sa->sa_handler = NULL;
-/* force a supervisor-mode page-in of the signal handler to reduce races */
-		__asm__ __volatile__("movesb %0,%/d0": :"m" (*(char *)pc):"d0");
-		current->blocked |= sa->sa_mask;
-		oldmask |= sa->sa_mask;
+	/* Did we come from a system call? */
+	if (regs->orig_d0 >= 0) {
+		/* Restart the system call - no handlers present */
+		if (regs->d0 == -ERESTARTNOHAND ||
+		    regs->d0 == -ERESTARTSYS ||
+		    regs->d0 == -ERESTARTNOINTR) {
+			regs->d0 = regs->orig_d0;
+			regs->pc -= 2;
+		}
 	}
-	wrusp((unsigned long)frame);
-	regs->ptregs.pc = pc;
 
-	/*
-	 * if setup_frame saved some extra frame junk, we need to
-	 * skip over that stuff when doing the RTE.  This means we have
-	 * to move the machine portion of the stack frame to where the
-	 * "RTE" instruction expects it. The signal that we need to
-	 * do this is that regs->stkadj is nonzero.
-	 */
-	if (regs->ptregs.stkadj) {
-		struct frame *tregs =
-			(struct frame *)((ulong)regs + regs->ptregs.stkadj);
-#if DEBUG
-	  printk("Performing stackadjust=%04x\n", (unsigned)
-		 regs->ptregs.stkadj);
-#endif
+	/* If we are about to discard some frame stuff we must copy
+	   over the remaining frame. */
+	if (regs->stkadj) {
+		struct pt_regs *tregs =
+		  (struct pt_regs *) ((ulong) regs + regs->stkadj);
+
 		/* This must be copied with decreasing addresses to
-                   handle overlaps.  */
-		tregs->ptregs.vector = regs->ptregs.vector;
-		tregs->ptregs.format = regs->ptregs.format;
-		tregs->ptregs.pc = regs->ptregs.pc;
-		tregs->ptregs.sr = regs->ptregs.sr;
+		   handle overlaps.  */
+		tregs->vector = regs->vector;
+		tregs->format = regs->format;
+		tregs->pc = regs->pc;
+		tregs->sr = regs->sr;
 	}
-
-	return 1;
+	return 0;
 }
diff -urN pc-2.0.21/arch/m68k/kernel/sys_m68k.c linux-2.0.21/arch/m68k/kernel/sys_m68k.c
--- pc-2.0.21/arch/m68k/kernel/sys_m68k.c	Thu May 16 08:05:10 1996
+++ linux-2.0.21/arch/m68k/kernel/sys_m68k.c	Thu Sep 12 21:31:30 1996
@@ -6,7 +6,6 @@
  * platform.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
@@ -16,8 +15,10 @@
 #include <linux/stat.h>
 #include <linux/mman.h>
 
+#include <asm/setup.h>
 #include <asm/segment.h>
 #include <asm/cachectl.h>
+#include <asm/traps.h>
 
 /*
  * sys_pipe() is the normal C calling standard for creating
@@ -198,12 +199,12 @@
 			: "=d" (_tmp2)					\
 			: "a" (_tmp1));					\
   _mmusr = _tmp2;							\
-  if (0 /* XXX _mmusr & MMU_?_040 */)					\
+  if (!(_mmusr & MMU_R_040))						\
     (valid) = 0;							\
   else									\
     {									\
       (valid) = 1;							\
-      (paddr) = _mmusr & ~0xfff;					\
+      (paddr) = _mmusr & PAGE_MASK;					\
     }									\
 }
 
@@ -237,6 +238,8 @@
 
     case FLUSH_SCOPE_LINE:
       len >>= 4;
+      /* Find the physical address of the first mapped page in the
+	 address range.  */
       for (;;)
 	{
 	  virt_to_phys_040 (addr, paddr, valid);
@@ -244,8 +247,8 @@
 	    break;
 	  if (len <= PAGE_SIZE / 16)
 	    return 0;
-	  len -= PAGE_SIZE / 16;
-	  addr += PAGE_SIZE;
+	  len -= (PAGE_SIZE - (addr & PAGE_MASK)) / 16;
+	  addr = (addr + PAGE_SIZE) & PAGE_MASK;
 	}
       while (len--)
 	{
@@ -283,8 +286,8 @@
 			break;
 		      if (len <= PAGE_SIZE / 16)
 			return 0;
-		      len -= PAGE_SIZE / 16;
-		      addr += PAGE_SIZE;
+		      len -= (PAGE_SIZE - (addr & PAGE_MASK)) / 16;
+		      addr = (addr + PAGE_SIZE) & PAGE_MASK;
 		    }
 		}
 	      else
@@ -367,6 +370,8 @@
 
     case FLUSH_SCOPE_LINE:
       len >>= 4;
+      /* Find the physical address of the first mapped page in the
+	 address range.  */
       for (;;)
 	{
 	  virt_to_phys_060 (addr, paddr, valid);
@@ -374,8 +379,8 @@
 	    break;
 	  if (len <= PAGE_SIZE / 16)
 	    return 0;
-	  len -= PAGE_SIZE / 16;
-	  addr += PAGE_SIZE;
+	  len -= (PAGE_SIZE - (addr & PAGE_MASK)) / 16;
+	  addr = (addr + PAGE_SIZE) & PAGE_MASK;
 	}
       while (len--)
 	{
@@ -413,8 +418,8 @@
 			break;
 		      if (len <= PAGE_SIZE / 16)
 			return 0;
-		      len -= PAGE_SIZE / 16;
-		      addr += PAGE_SIZE;
+		      len -= (PAGE_SIZE - (addr & PAGE_MASK)) / 16;
+		      addr = (addr + PAGE_SIZE) & PAGE_MASK;
 		    }
 		}
 	      else
@@ -477,17 +482,15 @@
     {
       /* Verify that the specified address region actually belongs to
 	 this process.  */
-      vma = find_vma (current, addr);
+      vma = find_vma (current->mm, addr);
       if (vma == NULL || addr < vma->vm_start || addr + len > vma->vm_end)
 	return -EINVAL;
     }
 
-  switch (m68k_is040or060)
-    {
-    default: /* 030 */
-      /* Always flush the whole cache, everything else would not be
-	 worth the hassle.  */
-      __asm__ __volatile__
+  if (CPU_IS_020_OR_030) {
+    /* Always flush the whole cache, everything else would not be
+       worth the hassle.  */
+    __asm__ __volatile__
 	("movec %%cacr, %%d0\n\t"
 	 "or %0, %%d0\n\t"
 	 "movec %%d0, %%cacr"
@@ -495,12 +498,9 @@
 	 : "di" ((cache & FLUSH_CACHE_INSN ? 8 : 0)
 		 | (cache & FLUSH_CACHE_DATA ? 0x800 : 0))
 	 : "d0");
-      return 0;
-
-    case 4: /* 040 */
-      return cache_flush_040 (addr, scope, cache, len);
-
-    case 6: /* 060 */
-      return cache_flush_060 (addr, scope, cache, len);
-    }
+    return 0;
+  } else if (CPU_IS_040)
+    return cache_flush_040 (addr, scope, cache, len);
+  else if (CPU_IS_060)
+    return cache_flush_060 (addr, scope, cache, len);
 }
diff -urN pc-2.0.21/arch/m68k/kernel/time.c linux-2.0.21/arch/m68k/kernel/time.c
--- pc-2.0.21/arch/m68k/kernel/time.c	Thu May 16 08:05:10 1996
+++ linux-2.0.21/arch/m68k/kernel/time.c	Sat Aug 24 14:30:58 1996
@@ -32,7 +32,7 @@
  * timer_interrupt() needs to keep up the real-time clock,
  * as well as call the "do_timer()" routine every clocktick
  */
-static void timer_interrupt(int irq, struct pt_regs * regs, void *dummy)
+static void timer_interrupt(int irq, void *dummy, struct pt_regs * regs)
 {
 	/* last time the cmos clock got updated */
 	static long last_rtc_update=0;
diff -urN pc-2.0.21/arch/m68k/kernel/traps.c linux-2.0.21/arch/m68k/kernel/traps.c
--- pc-2.0.21/arch/m68k/kernel/traps.c	Mon May 20 06:54:26 1996
+++ linux-2.0.21/arch/m68k/kernel/traps.c	Mon Sep 16 23:53:34 1996
@@ -28,10 +28,10 @@
 #include <linux/string.h>
 #include <linux/linkage.h>
 
+#include <asm/setup.h>
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <asm/traps.h>
-#include <asm/bootinfo.h>
 #include <asm/pgtable.h>
 #include <asm/machdep.h>
 
@@ -77,8 +77,7 @@
                 vectors[VEC_INT7] = nmihandler;
         }
 
-#ifdef CONFIG_FPSP_040
-	if (m68k_is040or060 == 4) {
+	if (CPU_IS_040) {
 		/* set up FPSP entry points */
 		asmlinkage void dz_vec(void) asm ("dz");
 		asmlinkage void inex_vec(void) asm ("inex");
@@ -101,38 +100,34 @@
 		vectors[VEC_LINE11] = fline_vec;
 		vectors[VEC_FPUNSUP] = unsupp_vec;
 	}
-#endif
-#ifdef CONFIG_IFPSP_060
-	if (m68k_is040or060 == 6) {
-	  /* set up IFPSP entry points */
- 	  asmlinkage void snan_vec(void) asm ("_060_fpsp_snan");
- 	  asmlinkage void operr_vec(void) asm ("_060_fpsp_operr");
- 	  asmlinkage void ovfl_vec(void) asm ("_060_fpsp_ovfl");
- 	  asmlinkage void unfl_vec(void) asm ("_060_fpsp_unfl");
- 	  asmlinkage void dz_vec(void) asm ("_060_fpsp_dz");
- 	  asmlinkage void inex_vec(void) asm ("_060_fpsp_inex");
- 	  asmlinkage void fline_vec(void) asm ("_060_fpsp_fline");
- 	  asmlinkage void unsupp_vec(void) asm ("_060_fpsp_unsupp");
- 	  asmlinkage void effadd_vec(void) asm ("_060_fpsp_effadd");
-  
- 	  asmlinkage void unimp_vec(void) asm ("_060_isp_unimp");
-  
- 	  vectors[VEC_FPNAN] = snan_vec;
- 	  vectors[VEC_FPOE] = operr_vec;
- 	  vectors[VEC_FPOVER] = ovfl_vec;
- 	  vectors[VEC_FPUNDER] = unfl_vec;
- 	  vectors[VEC_FPDIVZ] = dz_vec;
- 	  vectors[VEC_FPIR] = inex_vec;
- 	  vectors[VEC_LINE11] = fline_vec;
- 	  vectors[VEC_FPUNSUP] = unsupp_vec;
- 	  vectors[VEC_UNIMPEA] = effadd_vec;
-  
- 	  /* set up ISP entry points */
-  
- 	  vectors[VEC_UNIMPII] = unimp_vec;
-  
-  	}
-#endif
+	if (CPU_IS_060) {
+		/* set up IFPSP entry points */
+		asmlinkage void snan_vec(void) asm ("_060_fpsp_snan");
+		asmlinkage void operr_vec(void) asm ("_060_fpsp_operr");
+		asmlinkage void ovfl_vec(void) asm ("_060_fpsp_ovfl");
+		asmlinkage void unfl_vec(void) asm ("_060_fpsp_unfl");
+		asmlinkage void dz_vec(void) asm ("_060_fpsp_dz");
+		asmlinkage void inex_vec(void) asm ("_060_fpsp_inex");
+		asmlinkage void fline_vec(void) asm ("_060_fpsp_fline");
+		asmlinkage void unsupp_vec(void) asm ("_060_fpsp_unsupp");
+		asmlinkage void effadd_vec(void) asm ("_060_fpsp_effadd");
+
+		asmlinkage void unimp_vec(void) asm ("_060_isp_unimp");
+
+		vectors[VEC_FPNAN] = snan_vec;
+		vectors[VEC_FPOE] = operr_vec;
+		vectors[VEC_FPOVER] = ovfl_vec;
+		vectors[VEC_FPUNDER] = unfl_vec;
+		vectors[VEC_FPDIVZ] = dz_vec;
+		vectors[VEC_FPIR] = inex_vec;
+		vectors[VEC_LINE11] = fline_vec;
+		vectors[VEC_FPUNSUP] = unsupp_vec;
+		vectors[VEC_UNIMPEA] = effadd_vec;
+
+		/* set up ISP entry points */
+
+		vectors[VEC_UNIMPII] = unimp_vec;
+	}
 }
 
 void set_evector(int vecnum, void (*handler)(void))
@@ -176,10 +171,11 @@
 
 extern void die_if_kernel(char *,struct pt_regs *,int);
 asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address,
-			      unsigned long error_code);
+                             unsigned long error_code);
 
 asmlinkage void trap_c(struct frame *fp);
 
+#if defined (CONFIG_M68060)
 static inline void access_error060 (struct frame *fp)
 {
 	unsigned long fslw = fp->un.fmt4.pc; /* is really FSLW for access error */
@@ -208,7 +204,7 @@
 		printk("errorcode = %d\n", errorcode );
 #endif
 		if (fslw & MMU060_MA)
-		  addr = PAGE_ALIGN(addr);
+			addr = PAGE_ALIGN(addr);
 		do_page_fault( (struct pt_regs *)fp, addr, errorcode );
 	}
 	else {
@@ -216,7 +212,9 @@
 		trap_c( fp );
 	}
 }
+#endif /* CONFIG_M68060 */
 
+#if defined (CONFIG_M68040)
 static unsigned long probe040 (int iswrite, int fc, unsigned long addr)
 {
 	unsigned long mmusr;
@@ -224,8 +222,8 @@
 
 	set_fs (fc);
 
-  	if (iswrite)
-  		/* write */
+	if (iswrite)
+		/* write */
 		asm volatile ("movel %1,%/a0\n\t"
 			      ".word 0xf548\n\t"	/* ptestw (a0) */
 			      ".long 0x4e7a8805\n\t"	/* movec mmusr,a0 */
@@ -242,7 +240,6 @@
 			      : "g" (addr)
 			      : "a0");
 
-
 	set_fs (fs);
 
 	return mmusr;
@@ -351,14 +348,19 @@
 				 fp->un.fmt7.wb3a, fp->un.fmt7.wb3d,
 				 fp);
 }
+#endif /* CONFIG_M68040 */
 
+#if defined(CONFIG_M68020_OR_M68030)
 static inline void bus_error030 (struct frame *fp)
 {
 	volatile unsigned short temp;
 	unsigned short mmusr;
-	unsigned long addr, desc, errorcode;
+	unsigned long addr, errorcode;
 	unsigned short ssw = fp->un.fmtb.ssw;
 	int user_space_fault = 1;
+#if DEBUG
+	unsigned long desc;
+#endif
 
 #if DEBUG
 	printk ("pid = %x  ", current->pid);
@@ -414,14 +416,20 @@
 	  {
 	    addr = fp->un.fmtb.daddr;
 
+	    mmusr = MMU_I;
 	    if (user_space_fault) {
+#if DEBUG
 		    asm volatile ("ptestr #1,%2@,#7,%0\n\t"
 				  "pmove %/psr,%1@"
 				  : "=a&" (desc)
 				  : "a" (&temp), "a" (addr));
+#else
+		    asm volatile ("ptestr #1,%1@,#7\n\t"
+				  "pmove %/psr,%0@"
+				  : : "a" (&temp), "a" (addr));
+#endif
 		    mmusr = temp;
-	    } else
-		    mmusr = MMU_I;
+	    }
       
 #if DEBUG
 	    printk ("mmusr is %#x for addr %#lx in task %p\n",
@@ -431,16 +439,10 @@
 #endif
 
 	    errorcode = (mmusr & MMU_I) ? 0 : 1;
-	      /* if (!(ssw & RW)) updated to 1.2.13pl6 */
- 	    if (!(ssw & RW) || ssw & RM)
+	    if (!(ssw & RW) || ssw & RM)
 		    errorcode |= 2;
 
-	    if (mmusr & MMU_I)
-		    do_page_fault ((struct pt_regs *)fp, addr, errorcode);
-
-	    /* else if ((mmusr & MMU_WP) && !(ssw & RW)) */
-
- 	    else if ((mmusr & MMU_WP) && (!(ssw & RW) || ssw & RM))
+	    if (mmusr & (MMU_I | MMU_WP))
 		    do_page_fault ((struct pt_regs *)fp, addr, errorcode);
 	    else if (mmusr & (MMU_B|MMU_L|MMU_S)) {
 		    printk ("invalid %s access at %#lx from pc %#lx\n",
@@ -450,7 +452,7 @@
 		    force_sig(SIGSEGV, current);
 		    return;
 	    } else {
-#ifdef DEBUG
+#if 0
 		    static volatile long tlong;
 #endif
 
@@ -484,12 +486,13 @@
 
 	    /* setup an ATC entry for the access about to be retried */
 	    if (!(ssw & RW))
-		    asm volatile ("ploadw #1,%0@" : /* no outputs */
-				  : "a" (addr));
+		    asm volatile ("ploadw %1,%0@" : /* no outputs */
+				  : "a" (addr), "d" (ssw));
 	    else
-		    asm volatile ("ploadr #1,%0@" : /* no outputs */
-				  : "a" (addr));
+		    asm volatile ("ploadr %1,%0@" : /* no outputs */
+				  : "a" (addr), "d" (ssw));
 
+#if 0
 	    /* If this was a data fault due to an invalid page and a
 	       prefetch is pending on the same page, simulate it (but
 	       only if the page is now valid).  Otherwise we'll get an
@@ -525,38 +528,41 @@
 		      }
 		  }
 	      }
+#endif
 	  }
 
 	/* Now handle the instruction fault. */
 
+	if (!(ssw & (FC|FB)))
+		return;
+
 	/* get the fault address */
-	if ((fp->ptregs.format) == 0xA )
-		if (ssw & FC)
-			addr = fp->ptregs.pc + 2;
-		else if (ssw & FB)
-			addr = fp->ptregs.pc + 4;
-		else
-			return;
+	if (fp->ptregs.format == 10)
+		addr = fp->ptregs.pc + 4;
 	else
-		if (ssw & FC)
-			addr = fp->un.fmtb.baddr - 2;
-		else if (ssw & FB)
-			addr = fp->un.fmtb.baddr;
-		else
-			return;
+		addr = fp->un.fmtb.baddr;
+	if (ssw & FC)
+		addr -= 2;
 
 	if ((ssw & DF) && ((addr ^ fp->un.fmtb.daddr) & PAGE_MASK) == 0)
-		/* Insn fault on same page as data fault */
-		return;
+		/* Insn fault on same page as data fault.  But we
+		   should still create the ATC entry.  */
+		goto create_atc_entry;
 
+	mmusr = MMU_I;
 	if (user_space_fault) {
+#if DEBUG
 		asm volatile ("ptestr #1,%2@,#7,%0\n\t"
 			      "pmove %/psr,%1@"
 			      : "=a&" (desc)
 			      : "a" (&temp), "a" (addr));
+#else
+		asm volatile ("ptestr #1,%1@,#7\n\t"
+			      "pmove %/psr,%0@"
+			      : : "a" (&temp), "a" (addr));
+#endif
 		mmusr = temp;
-	} else
-		mmusr = MMU_I;
+	}
       
 #ifdef DEBUG
 	printk ("mmusr is %#x for addr %#lx in task %p\n",
@@ -565,10 +571,8 @@
 		mm_ptov(desc), *(unsigned long *)mm_ptov(desc));
 #endif
 
-	errorcode = (mmusr & MMU_I) ? 0 : 1;
-
 	if (mmusr & MMU_I)
-		do_page_fault ((struct pt_regs *)fp, addr, errorcode);
+		do_page_fault ((struct pt_regs *)fp, addr, 0);
 	else if (mmusr & (MMU_B|MMU_L|MMU_S)) {
 		printk ("invalid insn access at %#lx from pc %#lx\n",
 			addr, fp->ptregs.pc);
@@ -579,6 +583,8 @@
 		force_sig(SIGSEGV, current);
 		return;
 	} else {
+#if 0 /* stale ATC entry??  Ignore it */
+
 #ifdef DEBUG
 		static volatile long tlong;
 #endif
@@ -605,18 +611,22 @@
 		}
 
 #endif
+
 #if DEBUG
 		printk("Unknown SIGSEGV - 3\n");
 #endif
 		die_if_kernel("Oops",&fp->ptregs,mmusr);
 		force_sig(SIGSEGV, current);
 		return;
+#endif
 	}
 
+create_atc_entry:
 	/* setup an ATC entry for the access about to be retried */
-	asm volatile ("ploadr #1,%0@" : /* no outputs */
+	asm volatile ("ploadr #2,%0@" : /* no outputs */
 		      : "a" (addr));
 }
+#endif /* CONFIG_M68020_OR_M68030 */
 
 asmlinkage void buserr_c(struct frame *fp)
 {
@@ -629,16 +639,22 @@
 #endif
 
 	switch (fp->ptregs.format) {
+#if defined (CONFIG_M68060)
 	case 4:				/* 68060 access error */
 	  access_error060 (fp);
 	  break;
+#endif
+#if defined (CONFIG_M68040)
 	case 0x7:			/* 68040 access error */
 	  access_error040 (fp);
 	  break;
+#endif
+#if defined (CONFIG_M68020_OR_M68030)
 	case 0xa:
 	case 0xb:
 	  bus_error030 (fp);
 	  break;
+#endif
 	default:
 	  die_if_kernel("bad frame format",&fp->ptregs,0);
 #if DEBUG
@@ -673,7 +689,7 @@
 	    addr += sizeof(fp->un.fmt3);
 	    break;
 	case 0x4:
-	    printk((m68k_is040or060 == 6 ? "fault addr=%08lx fslw=%08lx\n"
+	    printk((CPU_IS_060 ? "fault addr=%08lx fslw=%08lx\n"
 		    : "eff addr=%08lx pc=%08lx\n"),
 		   fp->un.fmt4.effaddr, fp->un.fmt4.pc);
 	    addr += sizeof(fp->un.fmt4);
@@ -767,8 +783,7 @@
 		printk ("*** Exception %d ***   FORMAT=%X\n",
 			(fp->ptregs.vector) >> 2, 
 			fp->ptregs.format);
-	if (((fp->ptregs.vector) >> 2) == VEC_ADDRERR
-	    && !m68k_is040or060) {
+	if (fp->ptregs.vector >> 2 == VEC_ADDRERR && CPU_IS_020_OR_030) {
 		unsigned short ssw = fp->un.fmtb.ssw;
 
 		printk ("SSW=%#06x  ", ssw);
@@ -871,7 +886,7 @@
 		break;
 	}
 
-	force_sig (sig, current);
+	send_sig (sig, current, 1);
 }
 
 asmlinkage void set_esp0 (unsigned long ssp)
diff -urN pc-2.0.21/arch/m68k/lib/Makefile linux-2.0.21/arch/m68k/lib/Makefile
--- pc-2.0.21/arch/m68k/lib/Makefile	Mon Apr  1 20:37:48 1996
+++ linux-2.0.21/arch/m68k/lib/Makefile	Mon Sep 16 23:53:36 1996
@@ -2,7 +2,10 @@
 # Makefile for m68k-specific library files..
 #
 
+.S.o:
+	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $@
+
 L_TARGET = lib.a
-L_OBJS  = ashrdi3.o checksum.o memcpy.o memcmp.o
+L_OBJS  = ashrdi3.o checksum.o memcpy.o memcmp.o memset.o semaphore.o
 
 include $(TOPDIR)/Rules.make
diff -urN pc-2.0.21/arch/m68k/lib/memcpy.c linux-2.0.21/arch/m68k/lib/memcpy.c
--- pc-2.0.21/arch/m68k/lib/memcpy.c	Wed Dec 27 21:46:35 1995
+++ linux-2.0.21/arch/m68k/lib/memcpy.c	Wed Aug 28 00:14:10 1996
@@ -3,7 +3,7 @@
 void * memcpy(void * to, const void * from, size_t n)
 {
   void *xto = to;
-  size_t temp;
+  size_t temp, temp1;
 
   if (!n)
     return xto;
@@ -30,10 +30,30 @@
     {
       long *lto = to;
       const long *lfrom = from;
-      temp--;
-      do
-	*lto++ = *lfrom++;
-      while (temp--);
+
+      __asm__ __volatile__("movel %2,%3\n\t"
+			   "andw  #7,%3\n\t"
+			   "lsrl  #3,%2\n\t"
+			   "negw  %3\n\t"
+			   "jmp   %%pc@(1f,%3:w:2)\n\t"
+			   "4:\t"
+			   "movel %0@+,%1@+\n\t"
+			   "movel %0@+,%1@+\n\t"
+			   "movel %0@+,%1@+\n\t"
+			   "movel %0@+,%1@+\n\t"
+			   "movel %0@+,%1@+\n\t"
+			   "movel %0@+,%1@+\n\t"
+			   "movel %0@+,%1@+\n\t"
+			   "movel %0@+,%1@+\n\t"
+			   "1:\t"
+			   "dbra  %2,4b\n\t"
+			   "clrw  %2\n\t"
+			   "subql #1,%2\n\t"
+			   "jpl   4b\n\t"
+			   : "=a" (lfrom), "=a" (lto), "=d" (temp),
+			   "=&d" (temp1)
+			   : "0" (lfrom), "1" (lto), "2" (temp)
+			   );
       to = lto;
       from = lfrom;
     }
diff -urN pc-2.0.21/arch/m68k/lib/memset.c linux-2.0.21/arch/m68k/lib/memset.c
--- pc-2.0.21/arch/m68k/lib/memset.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/arch/m68k/lib/memset.c	Tue Sep  3 23:38:57 1996
@@ -0,0 +1,68 @@
+#include <linux/types.h>
+
+void * memset(void * s, int c, size_t count)
+{
+  void *xs = s;
+  size_t temp, temp1;
+
+  if (!count)
+    return xs;
+  c &= 0xff;
+  c |= c << 8;
+  c |= c << 16;
+  if ((long) s & 1)
+    {
+      char *cs = s;
+      *cs++ = c;
+      s = cs;
+      count--;
+    }
+  if (count > 2 && (long) s & 2)
+    {
+      short *ss = s;
+      *ss++ = c;
+      s = ss;
+      count -= 2;
+    }
+  temp = count >> 2;
+  if (temp)
+    {
+      long *ls = s;
+
+      __asm__ __volatile__("movel %1,%2\n\t"
+			   "andw  #7,%2\n\t"
+			   "lsrl  #3,%1\n\t"
+			   "negw  %2\n\t"
+			   "jmp   %%pc@(2f,%2:w:2)\n\t"
+			   "1:\t"
+			   "movel %3,%0@+\n\t"
+			   "movel %3,%0@+\n\t"
+			   "movel %3,%0@+\n\t"
+			   "movel %3,%0@+\n\t"
+			   "movel %3,%0@+\n\t"
+			   "movel %3,%0@+\n\t"
+			   "movel %3,%0@+\n\t"
+			   "movel %3,%0@+\n\t"
+			   "2:\t"
+			   "dbra  %1,1b\n\t"
+			   "clrw  %1\n\t"
+			   "subql #1,%1\n\t"
+			   "jpl   1b\n\t"
+			   : "=a" (ls), "=d" (temp), "=&d" (temp1)
+			   : "d" (c), "0" (ls), "1" (temp)
+			   );
+      s = ls;
+    }
+  if (count & 2)
+    {
+      short *ss = s;
+      *ss++ = c;
+      s = ss;
+    }
+  if (count & 1)
+    {
+      char *cs = s;
+      *cs = c;
+    }
+  return xs;
+}
diff -urN pc-2.0.21/arch/m68k/lib/semaphore.S linux-2.0.21/arch/m68k/lib/semaphore.S
--- pc-2.0.21/arch/m68k/lib/semaphore.S	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/arch/m68k/lib/semaphore.S	Wed Sep 18 19:05:01 1996
@@ -0,0 +1,29 @@
+/*
+ *  linux/arch/m68k/lib/semaphore.S
+ *
+ *  Copyright (C) 1996  Linus Torvalds
+ *
+ *  m68k version by Andreas Schwab
+ */
+
+#include <linux/linkage.h>
+
+/*
+ * "down_failed" is called with the eventual return address
+ * in %a0, and the address of the semaphore in %a1. We need
+ * to increment the number of waiters on the semaphore,
+ * call "__down()", and then eventually return to try again.
+ */
+ENTRY(down_failed)
+	movel %a0,-(%sp)
+	movel %a1,-(%sp)
+	jbsr SYMBOL_NAME(__down)
+	movel (%sp)+,%a1
+	rts
+
+ENTRY(up_wakeup)
+	movel %a0,-(%sp)
+	movel %a1,-(%sp)
+	jbsr SYMBOL_NAME(__up)
+	movel (%sp)+,%a1
+	rts
diff -urN pc-2.0.21/arch/m68k/mm/fault.c linux-2.0.21/arch/m68k/mm/fault.c
--- pc-2.0.21/arch/m68k/mm/fault.c	Mon May  6 11:44:30 1996
+++ linux-2.0.21/arch/m68k/mm/fault.c	Thu Sep 12 21:37:22 1996
@@ -29,14 +29,17 @@
 			      unsigned long error_code)
 {
 	struct vm_area_struct * vma;
+	struct task_struct *tsk = current;
+	struct mm_struct *mm = tsk->mm;
 
 #ifdef DEBUG
 	printk ("regs->sr=%#x, regs->pc=%#lx, address=%#lx, %ld, %p\n",
 		regs->sr, regs->pc, address, error_code,
-		current->tss.pagedir_v);
+		tsk->tss.pagedir_v);
 #endif
 
-	vma = find_vma(current, address);
+	down(&mm->mmap_sem);
+	vma = find_vma(mm, address);
 	if (!vma)
 	  goto bad_area;
 	if (vma->vm_start <= address)
@@ -73,17 +76,18 @@
 	    goto bad_area;
 	}
 	if (error_code & 1) {
-		do_wp_page(current, vma, address, error_code & 2);
+		do_wp_page(tsk, vma, address, error_code & 2);
+		up(&mm->mmap_sem);
 		return 0;
 	}
-	do_no_page(current, vma, address, error_code & 2);
+	do_no_page(tsk, vma, address, error_code & 2);
+	up(&mm->mmap_sem);
 
 	/* There seems to be a missing invalidate somewhere in do_no_page.
 	 * Until I found it, this one cures the problem and makes
 	 * 1.2 run on the 68040 (Martin Apel).
 	 */
-	flush_tlb_all();
-
+	flush_tlb_page(vma, address);
 	return 0;
 
 /*
@@ -91,9 +95,10 @@
  * Fix it, but check if it's kernel or user first..
  */
 bad_area:
+	up(&mm->mmap_sem);
 	if (user_mode(regs)) {
 		/* User memory access */
-		force_sig (SIGSEGV, current);
+		force_sig (SIGSEGV, tsk);
 		return 1;
 	}
 
@@ -111,3 +116,4 @@
 
 	return 1;
 }
+
diff -urN pc-2.0.21/arch/m68k/mm/init.c linux-2.0.21/arch/m68k/mm/init.c
--- pc-2.0.21/arch/m68k/mm/init.c	Thu May 16 08:05:10 1996
+++ linux-2.0.21/arch/m68k/mm/init.c	Sat Sep 14 15:17:41 1996
@@ -16,21 +16,16 @@
 #include <linux/blk.h>
 #endif
 
+#include <asm/setup.h>
 #include <asm/segment.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/system.h>
-#include <asm/bootinfo.h>
 #include <asm/machdep.h>
 
 extern void die_if_kernel(char *,struct pt_regs *,long);
 extern void init_kpointer_table(void);
 extern void show_net_buffers(void);
-extern unsigned long mm_phys_to_virt (unsigned long addr);
-extern char *rd_start;
-extern int rd_doload;
-
-unsigned long ramdisk_length;
 
 /*
  * BAD_PAGE is the page that is used for page faults when linux
@@ -94,14 +89,12 @@
 #endif
 }
 
-#if 0 /* The 68030 doesn't care about reserved bits. */
 /*
  * Bits to add to page descriptors for "normal" caching mode.
  * For 68020/030 this is 0.
  * For 68040, this is _PAGE_CACHE040 (cachable, copyback)
  */
-unsigned long mm_cachebits;
-#endif
+unsigned long mm_cachebits = 0;
 
 pte_t *kernel_page_table (unsigned long *memavailp)
 {
@@ -122,7 +115,6 @@
 #define ONEMEG	(1024*1024)
 #define L3TREESIZE (256*1024)
 
-	int is040 = m68k_is040or060;
 	static unsigned long mem_mapped = 0;
 	static unsigned long virtaddr = 0;
 	static pte_t *ktablep = NULL;
@@ -158,7 +150,7 @@
 	 * arch/head.S
 	 *
 	 */
-	if (is040 && mem_mapped == 0)
+	if (CPU_IS_040_OR_060 && mem_mapped == 0)
 		ktablep = kpt;
 
 	for (physaddr = addr;
@@ -180,7 +172,7 @@
 			pindex = 0;
 		}
 
-		if (is040) {
+		if (CPU_IS_040_OR_060) {
 			int i;
 			unsigned long ktable;
 
@@ -306,30 +298,14 @@
 #endif
 
 	init_kpointer_table();
-#if 0
-	/*
-	 * Setup cache bits
-	 */
-	mm_cachebits = m68k_is040or060 ? _PAGE_CACHE040 : 0;
 
-	/* Initialize protection map.  */
-	protection_map[0] = PAGE_READONLY;
-	protection_map[1] = PAGE_READONLY;
-	protection_map[2] = PAGE_COPY;
-	protection_map[3] = PAGE_COPY;
-	protection_map[4] = PAGE_READONLY;
-	protection_map[5] = PAGE_READONLY;
-	protection_map[6] = PAGE_COPY;
-	protection_map[7] = PAGE_COPY;
-	protection_map[8] = PAGE_READONLY;
-	protection_map[9] = PAGE_READONLY;
-	protection_map[10] = PAGE_SHARED;
-	protection_map[11] = PAGE_SHARED;
-	protection_map[12] = PAGE_READONLY;
-	protection_map[13] = PAGE_READONLY;
-	protection_map[14] = PAGE_SHARED;
-	protection_map[15] = PAGE_SHARED;
-#endif
+	/* Fix the cache mode in the page descriptors for the 680[46]0.  */
+	if (CPU_IS_040_OR_060) {
+		int i;
+		mm_cachebits = _PAGE_CACHE040;
+		for (i = 0; i < 16; i++)
+			pgprot_val(protection_map[i]) |= _PAGE_CACHE040;
+	}
 
 	/*
 	 * Map the physical memory available into the kernel virtual
@@ -394,17 +370,18 @@
 	task[0]->tss.crp[0] = 0x80000000 | _PAGE_SHORT;
 	task[0]->tss.crp[1] = task[0]->tss.pagedir_p;
 
-	if (m68k_is040or060)
-		asm ("movel %0,%/d0\n\t"
-		     ".long 0x4e7b0806" /* movec d0,urp */
-		     : /* no outputs */
-		     : "g" (task[0]->tss.crp[1])
-		     : "d0");
+	if (CPU_IS_040_OR_060)
+		asm __volatile__ ("movel %0,%/d0\n\t"
+				  ".long 0x4e7b0806" /* movec d0,urp */
+				  : /* no outputs */
+				  : "g" (task[0]->tss.crp[1])
+				  : "d0");
 	else
-		asm ("pmove %0@,%/crp"
-		     : /* no outputs */
-		     : "a" (task[0]->tss.crp));
-
+		asm __volatile__ ("movel %0,%/a0\n\t"
+				  ".long 0xf0104c00" /* pmove %/a0@,%/crp */
+				  : /* no outputs */
+				  : "g" (task[0]->tss.crp)
+				  : "a0");
 #ifdef DEBUG
 	printk ("set crp\n");
 #endif
@@ -418,34 +395,6 @@
 	printk ("before free_area_init\n");
 #endif
 
-#ifdef CONFIG_BLK_DEV_RAM
-#ifndef CONFIG_BLK_DEV_INITRD
-	/*
-	 * Since the initialization of the ramdisk's has been changed
-	 * so it fits the new driver initialization scheme, we have to
-	 * make room for our preloaded image here, instead of doing it
-	 * in rd_init() as we cannot kmalloc() a block large enough
-	 * for the image.
-	 */
-
-	ramdisk_length = boot_info.ramdisk_size * 1024;
-
-	if ((ramdisk_length > 0) && (ROOT_DEV == 0)) {
-	  char *rdp;	     /* current location of ramdisk */
-
-	  rd_start = (char *) start_mem;
-
-	  /* get current address of ramdisk */
-	  rdp = (char *)mm_phys_to_virt (boot_info.ramdisk_addr);
-
-	  /* copy the ram disk image */
-	  memcpy (rd_start, rdp, ramdisk_length);
-	  start_mem += ramdisk_length;
-	  rd_doload = 1;     /* tell rd_load to load this thing */
-	}
-#endif
-#endif
-
 	return free_area_init (start_mem, end_mem);
 }
 
@@ -512,7 +461,8 @@
 		}
 		mem_map[MAP_NR(tmp)].count = 1;
 #ifdef CONFIG_BLK_DEV_INITRD
-		if (!initrd_start || (tmp < initrd_start || tmp >= initrd_end))
+		if (!initrd_start ||
+		    (tmp < (initrd_start & PAGE_MASK) || tmp >= initrd_end))
 #endif
 			free_page(tmp);
 	}
diff -urN pc-2.0.21/arch/m68k/mm/memory.c linux-2.0.21/arch/m68k/mm/memory.c
--- pc-2.0.21/arch/m68k/mm/memory.c	Thu May 16 08:05:10 1996
+++ linux-2.0.21/arch/m68k/mm/memory.c	Tue Sep  3 23:44:14 1996
@@ -10,13 +10,13 @@
 #include <linux/types.h>
 #include <linux/malloc.h>
 
+#include <asm/setup.h>
 #include <asm/segment.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/system.h>
 #include <asm/traps.h>
 #include <asm/amigahw.h>
-#include <asm/bootinfo.h>
 
 extern pte_t *kernel_page_table (unsigned long *memavailp);
 
@@ -275,7 +275,7 @@
 	/* not in one of the memory chunks; get the actual
 	 * physical address from the MMU.
 	 */
-	if (m68k_is040or060 == 6) {
+	if (CPU_IS_060) {
 	  unsigned long fs = get_fs();
 	  unsigned long  paddr;
 
@@ -294,7 +294,7 @@
 
 	  return paddr;
 
-	} else if (m68k_is040or060 == 4) {
+	} else if (CPU_IS_040) {
 	  unsigned long mmusr;
 	  unsigned long fs = get_fs();
 
@@ -414,17 +414,18 @@
 
 /* push and invalidate page in both caches */
 #define	pushcl040(paddr) do { push040((paddr));\
-			      if (m68k_is040or060 == 6) clear040((paddr));\
+			      if (CPU_IS_060) clear040((paddr));\
 			 } while(0)
 
 /* push page in both caches, invalidate in i-cache */
 #define	pushcli040(paddr) do { push040((paddr));\
-			       if (m68k_is040or060 == 6) cleari040((paddr));\
+			       if (CPU_IS_060) cleari040((paddr));\
 			  } while(0)
 
 /* push page defined by virtual address in both caches */
 #define	pushv040(vaddr) __asm__ __volatile__ ("movel %0,%/a0\n\t"\
 					      /* ptestr (a0) */\
+					      "nop\n\t"\
 					      ".word 0xf568\n\t"\
 					      /* movec mmusr,d0 */\
 					      ".long 0x4e7a0805\n\t"\
@@ -474,13 +475,30 @@
 
 void cache_clear (unsigned long paddr, int len)
 {
-    if (m68k_is040or060) {
-	/* ++roman: There have been too many problems with the CINV, it seems
-	 * to break the cache maintenance of DMAing drivers. I don't expect
-	 * too much overhead by using CPUSH instead.
+    if (CPU_IS_040_OR_060) {
+	/*
+	 * cwe need special treatment for the first page, in case it
+	 * is not page-aligned.
 	 */
+	if (paddr & (PAGE_SIZE - 1)){
+	    pushcl040(paddr);
+	    if (len <= PAGE_SIZE){
+		if (((paddr + len - 1) ^ paddr) & PAGE_MASK) {
+		    pushcl040(paddr + len - 1);
+		}
+		return;
+	    }else{
+		len -=PAGE_SIZE;
+		paddr += PAGE_SIZE;
+	    }
+	}
+		
 	while (len > PAGE_SIZE) {
+#if 0
 	    pushcl040(paddr);
+#else
+	    clear040(paddr);
+#endif
 	    len -= PAGE_SIZE;
 	    paddr += PAGE_SIZE;
 	}
@@ -492,22 +510,6 @@
 	    }
 	}
     }
-#if 0
-	/* on 68040, invalidate cache lines for pages in the range */
-	while (len > PAGE_SIZE) {
-	    clear040(paddr);
-	    len -= PAGE_SIZE;
-	    paddr += PAGE_SIZE;
-	    }
-	if (len > 0) {
-	    /* 0 < len <= PAGE_SIZE */
-	    clear040(paddr);
-	    if (((paddr + len - 1) / PAGE_SIZE) != (paddr / PAGE_SIZE)) {
-		/* a page boundary gets crossed at the end */
-		clear040(paddr + len - 1);
-		}
-	    }
-#endif
     else /* 68030 or 68020 */
 	asm volatile ("movec %/cacr,%/d0\n\t"
 		      "oriw %0,%/d0\n\t"
@@ -526,7 +528,7 @@
 
 void cache_push (unsigned long paddr, int len)
 {
-    if (m68k_is040or060) {
+    if (CPU_IS_040_OR_060) {
 	/*
          * on 68040 or 68060, push cache lines for pages in the range;
 	 * on the '040 this also invalidates the pushed lines, but not on
@@ -539,9 +541,6 @@
 	    }
 	if (len > 0) {
 	    pushcli040(paddr);
-#if 0
-	    if (((paddr + len - 1) / PAGE_SIZE) != (paddr / PAGE_SIZE)) {
-#endif
 	    if (((paddr + len - 1) ^ paddr) & PAGE_MASK) {
 		/* a page boundary gets crossed at the end */
 		pushcli040(paddr + len - 1);
@@ -578,7 +577,7 @@
 
 void cache_push_v (unsigned long vaddr, int len)
 {
-    if (m68k_is040or060 == 4) {
+    if (CPU_IS_040) {
 	/* on 68040, push cache lines for pages in the range */
 	while (len > PAGE_SIZE) {
 	    pushv040(vaddr);
@@ -587,16 +586,13 @@
 	    }
 	if (len > 0) {
 	    pushv040(vaddr);
-#if 0
-	    if (((vaddr + len - 1) / PAGE_SIZE) != (vaddr / PAGE_SIZE)) {
-#endif
 	    if (((vaddr + len - 1) ^ vaddr) & PAGE_MASK) {
 		/* a page boundary gets crossed at the end */
 		pushv040(vaddr + len - 1);
 		}
 	    }
 	}
-    else if (m68k_is040or060 == 6) {
+    else if (CPU_IS_060) {
 	/* on 68040, push cache lines for pages in the range */
 	while (len > PAGE_SIZE) {
 	    pushv060(vaddr);
@@ -670,7 +666,7 @@
 	 * mapped... */
 	size = (size + STEP_SIZE - 1) & ~(STEP_SIZE-1);
 
-	if (m68k_is040or060) {
+	if (CPU_IS_040_OR_060) {
 		prot = _PAGE_PRESENT | _PAGE_GLOBAL040;
 		switch( nocacheflag ) {
 		  case KERNELMAP_FULL_CACHING:
@@ -697,7 +693,7 @@
 	if (pgd_present(*page_dir)) {
 		kpointerp = (pmd_t *)pgd_page(*page_dir);
 		pindex = (vaddr >> 18) & 0x7f;
-		if (pindex != 0 && m68k_is040or060) {
+		if (pindex != 0 && CPU_IS_040_OR_060) {
 			if (pmd_present(*kpointerp))
 				ktablep = (pte_t *)pmd_page(*kpointerp);
 			else {
@@ -727,7 +723,7 @@
 			pindex = 0;
 		}
 
-		if (m68k_is040or060) {
+		if (CPU_IS_040_OR_060) {
 			int i;
 			unsigned long ktable;
 
@@ -838,7 +834,7 @@
 	pgd_t *dir = pgd_offset_k( address );
 	unsigned long end = address + size;
 	
-	if (m68k_is040or060) {
+	if (CPU_IS_040_OR_060) {
 		switch( cmode ) {
 		  case KERNELMAP_FULL_CACHING:
 			cmode = _PAGE_CACHE040;
diff -urN pc-2.0.21/arch/m68k/tools/amiga/Makefile linux-2.0.21/arch/m68k/tools/amiga/Makefile
--- pc-2.0.21/arch/m68k/tools/amiga/Makefile	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/arch/m68k/tools/amiga/Makefile	Thu Sep 26 19:52:32 1996
@@ -0,0 +1,22 @@
+
+CC =		m68k-cbm-amigados-gcc
+CFLAGS =	-Wall -O2
+
+
+All:		dmesg
+
+
+dmesg:		dmesg.c
+		$(CC) $(CFLAGS) -o dmesg dmesg.c -noixemul
+
+
+CC =		m68k-cbm-amigados-gcc
+CFLAGS =	-Wall -O2
+
+
+All:		dmesg
+
+
+dmesg:		dmesg.c
+		$(CC) $(CFLAGS) -o dmesg dmesg.c -noixemul
+
diff -urN pc-2.0.21/arch/m68k/tools/amiga/dmesg.c linux-2.0.21/arch/m68k/tools/amiga/dmesg.c
--- pc-2.0.21/arch/m68k/tools/amiga/dmesg.c	Mon May 20 06:54:27 1996
+++ linux-2.0.21/arch/m68k/tools/amiga/dmesg.c	Thu Sep  5 19:46:16 1996
@@ -7,10 +7,6 @@
  *		       (Geert.Uytterhoeven@cs.kuleuven.ac.be)
  *
  *
- *  Compilation (under AmigaOS):
- *
- *	gcc -o dmesg dmesg.c -noixemul -idirafter INCLUDE: -Wall -s -O3
- *
  *  Usage:
  *
  *	dmesg
diff -urN pc-2.0.21/drivers/block/Makefile linux-2.0.21/drivers/block/Makefile
--- pc-2.0.21/drivers/block/Makefile	Wed Aug  7 11:31:22 1996
+++ linux-2.0.21/drivers/block/Makefile	Sat Aug 24 14:33:05 1996
@@ -21,11 +21,54 @@
 MOD_LIST_NAME := BLOCK_MODULES
 LX_OBJS :=
 
-ifeq ($(CONFIG_BLK_DEV_FD),y)
-L_OBJS += floppy.o
+ifneq "$(ARCH)" "m68k"
+  ifeq ($(CONFIG_BLK_DEV_FD),y)
+    L_OBJS += floppy.o
+  else
+    ifeq ($(CONFIG_BLK_DEV_FD),m)
+      M_OBJS += floppy.o
+    endif
+  endif
 else
-  ifeq ($(CONFIG_BLK_DEV_FD),m)
-  M_OBJS += floppy.o
+  ifdef CONFIG_AMIGA
+    ifeq ($(CONFIG_BLK_DEV_FD),y)
+    L_OBJS += amiflop.o
+    else
+      ifeq ($(CONFIG_BLK_DEV_FD),m)
+      M_OBJS += amiflop.o
+      endif
+    endif
+  endif
+  ifdef CONFIG_ATARI
+    ifeq ($(CONFIG_BLK_DEV_FD),y)
+    L_OBJS += ataflop.o
+    else
+      ifeq ($(CONFIG_BLK_DEV_FD),m)
+      M_OBJS += ataflop.o
+      endif
+    endif
+    ifeq ($(CONFIG_ATARI_ACSI),y)
+      LX_OBJS += acsi.o
+    else
+      ifeq ($(CONFIG_ATARI_ACSI),m)
+      MX_OBJS += acsi.o
+      endif
+    endif
+    ifeq ($(CONFIG_ATARI_SLM),y)
+      L_OBJS += acsi_slm.o
+    else
+      ifeq ($(CONFIG_ATARI_SLM),m)
+      M_OBJS += acsi_slm.o
+      endif
+    endif
+  endif
+endif
+
+ifeq ($(CONFIG_AMIGA_Z2RAM),y)
+L_OBJS += z2ram.o
+else
+  ifeq ($(CONFIG_AMIGA_Z2RAM),m)
+  M_OBJS += z2ram.o
   endif
 endif
 
diff -urN pc-2.0.21/drivers/block/acsi.c linux-2.0.21/drivers/block/acsi.c
--- pc-2.0.21/drivers/block/acsi.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/block/acsi.c	Sat Aug 24 14:33:08 1996
@@ -0,0 +1,1856 @@
+/*
+ * acsi.c -- Device driver for Atari ACSI hard disks
+ *
+ * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ *
+ * Some parts are based on hd.c by Linus Thorvalds
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Still to in this file:
+ *  - If a command ends with an error status (!= 0), the following
+ *    REQUEST SENSE commands (4 to fill the ST-DMA FIFO) are done by
+ *    polling the _IRQ signal (not interrupt-driven). This should be
+ *    avoided in future because it takes up a non-neglectible time in
+ *    the interrupt service routine while interrupts are disabled.
+ *    Maybe a timer interrupt will get lost :-(
+ */
+
+/*
+ * General notes:
+ *
+ *  - All ACSI devices (disks, CD-ROMs, ...) use major number 28.
+ *    Minors are organized like it is with SCSI: The upper 4 bits
+ *    identify the device, the lower 4 bits the partition.
+ *    The device numbers (the upper 4 bits) are given in the same
+ *    order as the devices are found on the bus.
+ *  - Up to 8 LUNs are supported for each target (if CONFIG_ACSI_MULTI_LUN
+ *    is defined), but only a total of 16 devices (due to minor
+ *    numbers...). Note that Atari allows only a maximum of 4 targets
+ *    (i.e. controllers, not devices) on the ACSI bus!
+ *  - A optimizing scheme similar to SCSI scatter-gather is implemented.
+ *  - Removable media are supported. After a medium change to device
+ *    is reinitialized (partition check etc.). Also, if the device
+ *    knows the PREVENT/ALLOW MEDIUM REMOVAL command, the door should
+ *    be locked and unlocked when mounting the first or unmounting the
+ *    last filesystem on the device. The code is untested, because I
+ *    don't have a removable harddisk.
+ *
+ */
+
+#define MAJOR_NR ACSI_MAJOR
+ 
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/genhd.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/major.h>
+#include <linux/blk.h>
+#include <linux/malloc.h>
+
+#include <linux/interrupt.h>
+
+#include <asm/setup.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atari_acsi.h>
+#include <asm/atari_stdma.h>
+#include <asm/atari_rootsec.h>
+
+
+#define DEBUG
+#undef DEBUG_DETECT
+#undef NO_WRITE
+
+#define MAX_ERRORS     		8	/* Max read/write errors/sector */
+#define MAX_LUN				8	/* Max LUNs per target */
+#define MAX_DEV		   		16
+
+#define ACSI_BUFFER_SIZE	(16*1024) /* "normal" ACSI buffer size */
+#define ACSI_BUFFER_MINSIZE	(2048) 	  /* min. buf size if ext. DMA present */
+#define ACSI_BUFFER_SECTORS	(ACSI_BUFFER_SIZE/512)
+
+#define ACSI_TIMEOUT		(4*HZ)
+
+/* minimum delay between two commands */
+
+#define COMMAND_DELAY 500
+
+typedef enum {
+	NONE, HARDDISK, CDROM
+} ACSI_TYPE;
+
+struct acsi_info_struct {
+	ACSI_TYPE		type;			/* type of device */
+	unsigned		target;			/* target number */
+	unsigned		lun;			/* LUN in target controller */
+	unsigned		removable : 1;	/* Flag for removable media */
+	unsigned		read_only : 1;	/* Flag for read only devices */
+	unsigned		old_atari_disk : 1; /* Is an old Atari disk       */
+	unsigned		changed : 1;	/* Medium has been changed */
+	unsigned long 	size;			/* #blocks */
+} acsi_info[MAX_DEV];
+
+/*
+ *	SENSE KEYS
+ */
+
+#define NO_SENSE		0x00
+#define RECOVERED_ERROR 	0x01
+#define NOT_READY		0x02
+#define MEDIUM_ERROR		0x03
+#define HARDWARE_ERROR		0x04
+#define ILLEGAL_REQUEST 	0x05
+#define UNIT_ATTENTION		0x06
+#define DATA_PROTECT		0x07
+#define BLANK_CHECK		0x08
+#define COPY_ABORTED		0x0a
+#define ABORTED_COMMAND 	0x0b
+#define VOLUME_OVERFLOW 	0x0d
+#define MISCOMPARE		0x0e
+
+
+/*
+ *	DEVICE TYPES
+ */
+
+#define TYPE_DISK	0x00
+#define TYPE_TAPE	0x01
+#define TYPE_WORM	0x04
+#define TYPE_ROM	0x05
+#define TYPE_MOD	0x07
+#define TYPE_NO_LUN	0x7f
+
+/* The data returned by MODE SENSE differ between the old Atari
+ * harddisks and SCSI disks connected to ACSI. In the following, both
+ * formats are defined and some macros to operate on them potably.
+ */
+
+typedef struct {
+	unsigned long	dummy[2];
+	unsigned long	sector_size;
+	unsigned char	format_code;
+#define ATARI_SENSE_FORMAT_FIX	1	
+#define ATARI_SENSE_FORMAT_CHNG	2
+	unsigned char	cylinders_h;
+	unsigned char	cylinders_l;
+	unsigned char	heads;
+	unsigned char	reduced_h;
+	unsigned char	reduced_l;
+	unsigned char	precomp_h;
+	unsigned char	precomp_l;
+	unsigned char	landing_zone;
+	unsigned char	steprate;
+	unsigned char	type;
+#define ATARI_SENSE_TYPE_FIXCHNG_MASK		4
+#define ATARI_SENSE_TYPE_SOFTHARD_MASK		8
+#define ATARI_SENSE_TYPE_FIX				4
+#define ATARI_SENSE_TYPE_CHNG				0
+#define ATARI_SENSE_TYPE_SOFT				0
+#define ATARI_SENSE_TYPE_HARD				8
+	unsigned char	sectors;
+} ATARI_SENSE_DATA;
+
+#define ATARI_CAPACITY(sd) \
+	(((int)((sd).cylinders_h<<8)|(sd).cylinders_l) * \
+	 (sd).heads * (sd).sectors)
+
+
+typedef struct {
+	unsigned char   dummy1;
+	unsigned char   medium_type;
+	unsigned char   dummy2;
+	unsigned char   descriptor_size;
+	unsigned long   block_count;
+	unsigned long   sector_size;
+	/* Page 0 data */
+	unsigned char	page_code;
+	unsigned char	page_size;
+	unsigned char	page_flags;
+	unsigned char	qualifier;
+} SCSI_SENSE_DATA;
+
+#define SCSI_CAPACITY(sd) 	((sd).block_count & 0xffffff)
+
+
+typedef union {
+	ATARI_SENSE_DATA	atari;
+	SCSI_SENSE_DATA		scsi;
+} SENSE_DATA;
+
+#define SENSE_TYPE_UNKNOWN	0
+#define SENSE_TYPE_ATARI	1
+#define SENSE_TYPE_SCSI		2
+
+#define SENSE_TYPE(sd)										\
+	(((sd).atari.dummy[0] == 8 &&							\
+	  ((sd).atari.format_code == 1 ||						\
+	   (sd).atari.format_code == 2)) ? SENSE_TYPE_ATARI :	\
+	 ((sd).scsi.dummy1 >= 11) ? SENSE_TYPE_SCSI :			\
+	 SENSE_TYPE_UNKNOWN)
+	 
+#define CAPACITY(sd)							\
+	(SENSE_TYPE(sd) == SENSE_TYPE_ATARI ?		\
+	 ATARI_CAPACITY((sd).atari) :				\
+	 SCSI_CAPACITY((sd).scsi))
+
+#define SECTOR_SIZE(sd)							\
+	(SENSE_TYPE(sd) == SENSE_TYPE_ATARI ?		\
+	 (sd).atari.sector_size :					\
+	 (sd).scsi.sector_size & 0xffffff)
+
+/* Default size if capacity cannot be determined (1 GByte) */
+#define	DEFAULT_SIZE	0x1fffff
+
+#define CARTRCH_STAT(dev,buf)					\
+	(acsi_info[(dev)].old_atari_disk ?			\
+	 (((buf)[0] & 0x7f) == 0x28) :					\
+	 ((((buf)[0] & 0x70) == 0x70) ?					\
+	  (((buf)[2] & 0x0f) == 0x06) :					\
+	  (((buf)[0] & 0x0f) == 0x06)))					\
+
+/* These two are also exported to other drivers that work on the ACSI bus and
+ * need an ST-RAM buffer. */
+char 			*acsi_buffer;
+unsigned long 	phys_acsi_buffer;
+
+static int				NDevices = 0;
+static int				acsi_sizes[MAX_DEV<<4] = { 0, };
+static int				acsi_blocksizes[MAX_DEV<<4] = { 0, };
+static struct hd_struct	acsi_part[MAX_DEV<<4] = { {0,0}, };
+static int 				access_count[MAX_DEV] = { 0, };
+static char 			busy[MAX_DEV] = { 0, };
+static struct wait_queue *busy_wait;
+
+static int				CurrentNReq;
+static int				CurrentNSect;
+static char				*CurrentBuffer;
+
+
+#define SET_TIMER()							\
+	do {								\
+		del_timer( &acsi_timer );				\
+		acsi_timer.expires = jiffies + ACSI_TIMEOUT;		\
+		add_timer( &acsi_timer );				\
+	} while(0)
+
+#define CLEAR_TIMER()							\
+	do {								\
+		del_timer( &acsi_timer );				\
+	} while(0)
+
+static unsigned long	STramMask;
+#define STRAM_ADDR(a)	(((a) & STramMask) == 0)
+
+
+
+/* ACSI commands */
+
+static char tur_cmd[6]        = { 0x00, 0, 0, 0, 0, 0 };
+static char modesense_cmd[6]  = { 0x1a, 0, 0, 0, 24, 0 };
+static char modeselect_cmd[6] = { 0x15, 0, 0, 0, 12, 0 };
+static char inquiry_cmd[6]    = { 0x12, 0, 0, 0,255, 0 };
+static char reqsense_cmd[6]   = { 0x03, 0, 0, 0, 4, 0 };
+static char read_cmd[6]       = { 0x08, 0, 0, 0, 0, 0 };
+static char write_cmd[6]      = { 0x0a, 0, 0, 0, 0, 0 };
+static char pa_med_rem_cmd[6] = { 0x1e, 0, 0, 0, 0, 0 };
+
+#define CMDSET_TARG_LUN(cmd,targ,lun)			\
+    do {						\
+		cmd[0] = (cmd[0] & ~0xe0) | (targ)<<5;	\
+		cmd[1] = (cmd[1] & ~0xe0) | (lun)<<5;	\
+	} while(0)
+
+#define CMDSET_BLOCK(cmd,blk)						\
+    do {											\
+		unsigned long __blk = (blk);				\
+		cmd[3] = __blk; __blk >>= 8;				\
+		cmd[2] = __blk; __blk >>= 8;				\
+		cmd[1] = (cmd[1] & 0xe0) | (__blk & 0x1f);	\
+	} while(0)
+
+#define CMDSET_LEN(cmd,len)						\
+	do {										\
+		cmd[4] = (len);							\
+	} while(0)
+
+#define min(a,b)	(((a)<(b))?(a):(b))
+
+
+/* ACSI errors (from REQUEST SENSE); There are two tables, one for the
+ * old Atari disks and one for SCSI on ACSI disks.
+ */
+
+struct acsi_error {
+	unsigned char	code;
+	const char		*text;
+} atari_acsi_errors[] = {
+	{ 0x00, "No error (??)" },
+	{ 0x01, "No index pulses" },
+	{ 0x02, "Seek not complete" },
+	{ 0x03, "Write fault" },
+	{ 0x04, "Drive not ready" },
+	{ 0x06, "No Track 00 signal" },
+	{ 0x10, "ECC error in ID field" },
+	{ 0x11, "Uncorrectable data error" },
+	{ 0x12, "ID field address mark not found" },
+	{ 0x13, "Data field address mark not found" },
+	{ 0x14, "Record not found" },
+	{ 0x15, "Seek error" },
+	{ 0x18, "Data check in no retry mode" },
+	{ 0x19, "ECC error during verify" },
+	{ 0x1a, "Access to bad block" },
+	{ 0x1c, "Unformatted or bad format" },
+	{ 0x20, "Invalid command" },
+	{ 0x21, "Invalid block address" },
+	{ 0x23, "Volume overflow" },
+	{ 0x24, "Invalid argument" },
+	{ 0x25, "Invalid drive number" },
+	{ 0x26, "Byte zero parity check" },
+	{ 0x28, "Cartride changed" },
+	{ 0x2c, "Error count overflow" },
+	{ 0x30, "Controller selftest failed" }
+},
+
+	scsi_acsi_errors[] = {
+	{ 0x00, "No error (??)" },
+	{ 0x01, "Recovered error" },
+	{ 0x02, "Drive not ready" },
+	{ 0x03, "Uncorrectable medium error" },
+	{ 0x04, "Hardware error" },
+	{ 0x05, "Illegal request" },
+	{ 0x06, "Unit attention (Reset or cartridge changed)" },
+	{ 0x07, "Data protection" },
+	{ 0x08, "Blank check" },
+	{ 0x0b, "Aborted Command" },
+	{ 0x0d, "Volume overflow" }
+};
+
+
+
+/***************************** Prototypes *****************************/
+
+static int acsicmd_dma( const char *cmd, char *buffer, int blocks, int
+                        rwflag, int enable);
+static int acsi_reqsense( char *buffer, int targ, int lun);
+static void acsi_print_error( const unsigned char *errblk, int dev );
+static void acsi_interrupt (int irq, void *data, struct pt_regs *fp);
+static void unexpected_acsi_interrupt( void );
+static void bad_rw_intr( void );
+static void read_intr( void );
+static void write_intr( void);
+static void acsi_times_out( unsigned long dummy );
+static void copy_to_acsibuffer( void );
+static void copy_from_acsibuffer( void );
+static void do_end_requests( void );
+static void do_acsi_request( void );
+static void redo_acsi_request( void );
+static int acsi_ioctl( struct inode *inode, struct file *file, unsigned int
+                       cmd, unsigned long arg );
+static int acsi_open( struct inode * inode, struct file * filp );
+static void acsi_release( struct inode * inode, struct file * file );
+static void acsi_prevent_removal( int target, int flag );
+static int acsi_change_blk_size( int target, int lun);
+static int acsi_mode_sense( int target, int lun, SENSE_DATA *sd );
+static void acsi_geninit( struct gendisk *gd );
+static int revalidate_acsidisk( int dev, int maxusage );
+static int acsi_revalidate (dev_t);
+
+/************************* End of Prototypes **************************/
+
+
+struct timer_list acsi_timer = { NULL, NULL, 0, 0, acsi_times_out };
+
+
+#ifdef CONFIG_ATARI_SLM
+
+extern int N_SLM_Printers;
+extern void attach_slm( int target, int lun );
+extern int slm_init( void );
+
+#endif
+
+
+
+/***********************************************************************
+ *
+ *   ACSI primitives
+ *
+ **********************************************************************/
+
+
+/*
+ * The following two functions wait for _IRQ to become Low or High,
+ * resp., with a timeout. The 'timeout' parameter is in jiffies
+ * (10ms).
+ * If the functions are called with timer interrupts on (int level <
+ * 6), the timeout is based on the 'jiffies' variable to provide exact
+ * timeouts for device probing etc.
+ * If interrupts are disabled, the number of tries is based on the
+ * 'loops_per_sec' variable. A rough estimation is sufficient here...
+ */
+
+#define INT_LEVEL													\
+	({	unsigned __sr;												\
+		__asm__ __volatile__ ( "movew	%/sr,%0" : "=dm" (__sr) );	\
+		(__sr >> 8) & 7;											\
+	})
+
+int acsi_wait_for_IRQ( unsigned timeout )
+
+{
+	if (INT_LEVEL < 6) {
+		unsigned long maxjif;
+		for( maxjif = jiffies + timeout; jiffies < maxjif; )
+			if (!(mfp.par_dt_reg & 0x20)) return( 1 );
+	}
+	else {
+		long tries = loops_per_sec / HZ / 8 * timeout;
+		while( --tries >= 0 )
+			if (!(mfp.par_dt_reg & 0x20)) return( 1 );
+	}		
+	return( 0 ); /* timeout! */
+}
+
+
+int acsi_wait_for_noIRQ( unsigned timeout )
+
+{
+	if (INT_LEVEL < 6) {
+		unsigned long maxjif;
+		for( maxjif = jiffies + timeout; jiffies < maxjif; )
+			if (mfp.par_dt_reg & 0x20) return( 1 );
+	}
+	else {
+		long tries = loops_per_sec * timeout / HZ / 8;
+		while( tries-- >= 0 )
+			if (mfp.par_dt_reg & 0x20) return( 1 );
+	}		
+	return( 0 ); /* timeout! */
+}
+
+static struct timeval start_time;
+
+void
+acsi_delay_start(void)
+{
+	do_gettimeofday(&start_time);
+}
+
+/* wait from acsi_delay_start to now usec (<1E6) usec */
+
+void
+acsi_delay_end(long usec)
+{
+	struct timeval end_time;
+	long deltau,deltas;
+	do_gettimeofday(&end_time);
+	deltau=end_time.tv_usec - start_time.tv_usec;
+	deltas=end_time.tv_sec - start_time.tv_sec;
+	if (deltas > 1 || deltas < 0)
+		return;
+	if (deltas > 0)
+		deltau += 1000*1000;
+	if (deltau >= usec)
+		return;
+	udelay(usec-deltau);
+}
+
+/* acsicmd_dma() sends an ACSI command and sets up the DMA to transfer
+ * 'blocks' blocks of 512 bytes from/to 'buffer'.
+ * Because the _IRQ signal is used for handshaking the command bytes,
+ * the ACSI interrupt has to be disabled in this function. If the end
+ * of the operation should be signalled by a real interrupt, it has to be
+ * reenabled afterwards.
+ */
+
+static int acsicmd_dma( const char *cmd, char *buffer, int blocks, int rwflag, int enable)
+
+{	unsigned long	flags, paddr;
+	int				i;
+
+#ifdef NO_WRITE
+	if (rwflag || *cmd == 0x0a) {
+		printk( "ACSI: Write commands disabled!\n" );
+		return( 0 );
+	}
+#endif
+	
+	rwflag = rwflag ? 0x100 : 0;
+	paddr = VTOP( buffer );
+
+	acsi_delay_end(COMMAND_DELAY);
+	DISABLE_IRQ();
+
+	save_flags(flags);  
+	cli();
+	/* Low on A1 */
+	dma_wd.dma_mode_status = 0x88 | rwflag;
+	MFPDELAY();
+
+	/* set DMA address */
+	dma_wd.dma_lo = (unsigned char)paddr;
+	paddr >>= 8;
+	MFPDELAY();
+	dma_wd.dma_md = (unsigned char)paddr;
+	paddr >>= 8;
+	MFPDELAY();
+	if (ATARIHW_PRESENT(EXTD_DMA))
+		st_dma_ext_dmahi = (unsigned short)paddr;
+	else
+		dma_wd.dma_hi = (unsigned char)paddr;
+	MFPDELAY();
+	restore_flags(flags);
+
+	/* send the command bytes except the last */
+	for( i = 0; i < 5; ++i ) {
+		DMA_LONG_WRITE( *cmd++, 0x8a | rwflag );
+		udelay(20);
+		if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */
+	}
+
+	/* Clear FIFO and switch DMA to correct direction */  
+	dma_wd.dma_mode_status = 0x92 | (rwflag ^ 0x100);  
+	MFPDELAY();
+	dma_wd.dma_mode_status = 0x92 | rwflag;
+	MFPDELAY();
+
+	/* How many sectors for DMA */
+	dma_wd.fdc_acces_seccount = blocks;
+	MFPDELAY();
+	
+	/* send last command byte */
+	dma_wd.dma_mode_status = 0x8a | rwflag;
+	MFPDELAY();
+	DMA_LONG_WRITE( *cmd++, 0x0a | rwflag );
+	if (enable)
+		ENABLE_IRQ();
+	udelay(80);
+
+	return( 1 );
+}
+
+
+/*
+ * acsicmd_nodma() sends an ACSI command that requires no DMA.
+ */
+
+int acsicmd_nodma( const char *cmd, int enable)
+
+{	int	i;
+
+	acsi_delay_end(COMMAND_DELAY);
+	DISABLE_IRQ();
+
+	/* send first command byte */
+	dma_wd.dma_mode_status = 0x88;
+	MFPDELAY();
+	DMA_LONG_WRITE( *cmd++, 0x8a );
+	udelay(20);
+	if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */
+
+	/* send the intermediate command bytes */
+	for( i = 0; i < 4; ++i ) {
+		DMA_LONG_WRITE( *cmd++, 0x8a );
+		udelay(20);
+		if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */
+	}
+
+	/* send last command byte */
+	DMA_LONG_WRITE( *cmd++, 0x0a );
+	if (enable)
+		ENABLE_IRQ();
+	udelay(80);
+	
+	return( 1 );
+	/* Note that the ACSI interrupt is still disabled after this
+	 * function. If you want to get the IRQ delivered, enable it manually!
+	 */
+}
+
+
+static int acsi_reqsense( char *buffer, int targ, int lun)
+
+{
+	CMDSET_TARG_LUN( reqsense_cmd, targ, lun);
+	if (!acsicmd_dma( reqsense_cmd, buffer, 1, 0, 0 )) return( 0 );
+	if (!acsi_wait_for_IRQ( 10 )) return( 0 );
+	acsi_getstatus();
+	if (!acsicmd_nodma( reqsense_cmd, 0 )) return( 0 );
+	if (!acsi_wait_for_IRQ( 10 )) return( 0 );
+	acsi_getstatus();
+	if (!acsicmd_nodma( reqsense_cmd, 0 )) return( 0 );
+	if (!acsi_wait_for_IRQ( 10 )) return( 0 );
+	acsi_getstatus();
+	if (!acsicmd_nodma( reqsense_cmd, 0 )) return( 0 );
+	if (!acsi_wait_for_IRQ( 10 )) return( 0 );
+	acsi_getstatus();
+	dma_cache_maintenance( VTOP(buffer), 16, 0 );
+	
+	return( 1 );
+}	
+
+
+/*
+ * ACSI status phase: get the status byte from the bus
+ *
+ * I've seen several times that a 0xff status is read, propably due to
+ * a timing error. In this case, the procedure is repeated after the
+ * next _IRQ edge.
+ */
+
+int acsi_getstatus( void )
+
+{	int	status;
+
+	DISABLE_IRQ();
+	for(;;) {
+		if (!acsi_wait_for_IRQ( 100 )) {
+			acsi_delay_start();
+			return( -1 );
+		}
+		dma_wd.dma_mode_status = 0x8a;
+		MFPDELAY();
+		status = dma_wd.fdc_acces_seccount;
+		if (status != 0xff) break;
+#ifdef DEBUG
+		printk("ACSI: skipping 0xff status byte\n" );
+#endif
+		udelay(40);
+		acsi_wait_for_noIRQ( 20 );
+	}
+	dma_wd.dma_mode_status = 0x80;
+	udelay(40);
+	acsi_wait_for_noIRQ( 20 );
+
+	acsi_delay_start();
+	return( status & 0x1f ); /* mask of the device# */
+}
+
+
+#ifdef CONFIG_ATARI_SLM
+
+/* Receive data in an extended status phase. Needed by SLM printer. */
+
+int acsi_extstatus( char *buffer, int cnt )
+
+{	int	status;
+
+	DISABLE_IRQ();
+	udelay(80);
+	while( cnt-- > 0 ) {
+		if (!acsi_wait_for_IRQ( 40 )) return( 0 );
+		dma_wd.dma_mode_status = 0x8a;
+		MFPDELAY();
+		status = dma_wd.fdc_acces_seccount;
+		MFPDELAY();
+		*buffer++ = status & 0xff;
+		udelay(40);
+	}
+	return( 1 );
+}
+
+
+/* Finish an extended status phase */
+
+void acsi_end_extstatus( void )
+
+{
+	dma_wd.dma_mode_status = 0x80;
+	udelay(40);
+	acsi_wait_for_noIRQ( 20 );
+	acsi_delay_start();
+}
+
+
+/* Send data in an extended command phase */
+
+int acsi_extcmd( unsigned char *buffer, int cnt )
+
+{
+	while( cnt-- > 0 ) {
+		DMA_LONG_WRITE( *buffer++, 0x8a );
+		udelay(20);
+		if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */
+	}
+	return( 1 );
+}
+
+#endif
+
+
+static void acsi_print_error( const unsigned char *errblk, int dev  )
+
+{	int atari_err, i, errcode;
+	struct acsi_error *arr;
+
+	atari_err = acsi_info[dev].old_atari_disk;
+	if (atari_err)
+		errcode = errblk[0] & 0x7f;
+	else
+		if ((errblk[0] & 0x70) == 0x70)
+			errcode = errblk[2] & 0x0f;
+		else
+			errcode = errblk[0] & 0x0f;
+	
+	printk( KERN_ERR "ACSI error 0x%02x", errcode );
+
+	if (errblk[0] & 0x80)
+		printk( " for sector %d",
+				((errblk[1] & 0x1f) << 16) |
+				(errblk[2] << 8) | errblk[0] );
+
+	arr = atari_err ? atari_acsi_errors : scsi_acsi_errors;
+	i = atari_err ? sizeof(atari_acsi_errors)/sizeof(*atari_acsi_errors) :
+		            sizeof(scsi_acsi_errors)/sizeof(*scsi_acsi_errors);
+	
+	for( --i; i >= 0; --i )
+		if (arr[i].code == errcode) break;
+	if (i >= 0)
+		printk( ": %s\n", arr[i].text );
+}
+
+/*******************************************************************
+ *
+ * ACSI interrupt routine
+ *   Test, if this is a ACSI interrupt and call the irq handler
+ *   Otherwise ignore this interrupt.
+ *
+ *******************************************************************/
+
+static void acsi_interrupt(int irq, void *data, struct pt_regs *fp )
+
+{	void (*acsi_irq_handler)(void) = DEVICE_INTR;
+
+	DEVICE_INTR = NULL;
+	CLEAR_TIMER();
+
+	if (!acsi_irq_handler)
+		acsi_irq_handler = unexpected_acsi_interrupt;
+	acsi_irq_handler();
+}
+
+
+/******************************************************************
+ *
+ * The Interrupt handlers
+ *
+ *******************************************************************/
+
+
+static void unexpected_acsi_interrupt( void )
+
+{
+	printk( KERN_WARNING "Unexpected ACSI interrupt\n" );
+}
+
+
+/* This function is called in case of errors. Because we cannot reset
+ * the ACSI bus or a single device, there is no other choice than
+ * retrying several times :-(
+ */
+
+static void bad_rw_intr( void )
+
+{
+	if (!CURRENT)
+		return;
+
+	if (++CURRENT->errors >= MAX_ERRORS)
+		end_request(0);
+	/* Otherwise just retry */
+}
+
+
+static void read_intr( void )
+
+{	int		status;
+	
+	status = acsi_getstatus();
+	if (status != 0) {
+		int dev = DEVICE_NR(MINOR(CURRENT->rq_dev));
+		printk( KERN_ERR "ad%c: ", dev+'a' );
+		if (!acsi_reqsense( acsi_buffer, acsi_info[dev].target, 
+					acsi_info[dev].lun))
+			printk( "ACSI error and REQUEST SENSE failed (status=0x%02x)\n", status );
+		else {
+			acsi_print_error( acsi_buffer, dev );
+			if (CARTRCH_STAT( dev, acsi_buffer ))
+				acsi_info[dev].changed = 1;
+		}
+		ENABLE_IRQ();
+		bad_rw_intr();
+		redo_acsi_request();
+		return;
+	}
+
+	dma_cache_maintenance( VTOP(CurrentBuffer), CurrentNSect*512, 0 );
+	if (CurrentBuffer == acsi_buffer)
+		copy_from_acsibuffer();
+
+	do_end_requests();
+	redo_acsi_request();
+}
+
+
+static void write_intr(void)
+
+{	int	status;
+
+	status = acsi_getstatus();
+	if (status != 0) {
+		int	dev = DEVICE_NR(MINOR(CURRENT->rq_dev));
+		printk( KERN_ERR "ad%c: ", dev+'a' );
+		if (!acsi_reqsense( acsi_buffer, acsi_info[dev].target,
+					acsi_info[dev].lun))
+			printk( "ACSI error and REQUEST SENSE failed (status=0x%02x)\n", status );
+		else {
+			acsi_print_error( acsi_buffer, dev );
+			if (CARTRCH_STAT( dev, acsi_buffer ))
+				acsi_info[dev].changed = 1;
+		}
+		bad_rw_intr();
+		redo_acsi_request();
+		return;
+	}
+
+	do_end_requests();
+	redo_acsi_request();
+}
+
+
+static void acsi_times_out( unsigned long dummy )
+
+{
+	DISABLE_IRQ();
+	if (!DEVICE_INTR) return;
+
+	DEVICE_INTR = NULL;
+	printk( KERN_ERR "ACSI timeout\n" );
+	if (!CURRENT) return;
+	if (++CURRENT->errors >= MAX_ERRORS) {
+#ifdef DEBUG
+		printk( KERN_ERR "ACSI: too many errors.\n" );
+#endif
+		end_request(0);
+	}
+
+	redo_acsi_request();
+}
+
+
+
+/***********************************************************************
+ *
+ *  Scatter-gather utility functions
+ *
+ ***********************************************************************/
+
+
+static void copy_to_acsibuffer( void )
+
+{	int					i;
+	char				*src, *dst;
+	struct buffer_head	*bh;
+	
+	src = CURRENT->buffer;
+	dst = acsi_buffer;
+	bh = CURRENT->bh;
+
+	if (!bh)
+		memcpy( dst, src, CurrentNSect*512 );
+	else
+		for( i = 0; i < CurrentNReq; ++i ) {
+			memcpy( dst, src, bh->b_size );
+			dst += bh->b_size;
+			if ((bh = bh->b_reqnext))
+				src = bh->b_data;
+		}
+}
+
+
+static void copy_from_acsibuffer( void )
+
+{	int					i;
+	char				*src, *dst;
+	struct buffer_head	*bh;
+	
+	dst = CURRENT->buffer;
+	src = acsi_buffer;
+	bh = CURRENT->bh;
+
+	if (!bh)
+		memcpy( dst, src, CurrentNSect*512 );
+	else
+		for( i = 0; i < CurrentNReq; ++i ) {
+			memcpy( dst, src, bh->b_size );
+			src += bh->b_size;
+			if ((bh = bh->b_reqnext))
+				dst = bh->b_data;
+		}
+}
+
+
+static void do_end_requests( void )
+
+{	int		i, n;
+
+	if (!CURRENT->bh) {
+		CURRENT->nr_sectors -= CurrentNSect;
+		CURRENT->current_nr_sectors -= CurrentNSect;
+		CURRENT->sector += CurrentNSect;
+		if (CURRENT->nr_sectors == 0)
+			end_request(1);
+	}
+	else {
+		for( i = 0; i < CurrentNReq; ++i ) {
+			n = CURRENT->bh->b_size >> 9;
+			CURRENT->nr_sectors -= n;
+			CURRENT->current_nr_sectors -= n;
+			CURRENT->sector += n;
+			end_request(1);
+		}
+	}
+}
+
+
+
+
+/***********************************************************************
+ *
+ *  do_acsi_request and friends
+ *
+ ***********************************************************************/
+
+static void do_acsi_request( void )
+
+{
+	stdma_lock( acsi_interrupt, NULL );
+	redo_acsi_request();
+}
+
+
+static void redo_acsi_request( void )
+
+{	unsigned			block, dev, target, lun, nsect;
+	char 				*buffer;
+	unsigned long		pbuffer;
+	struct buffer_head	*bh;
+	
+	if (CURRENT && CURRENT->rq_status == RQ_INACTIVE) {
+		if (!DEVICE_INTR) {
+			ENABLE_IRQ();
+			stdma_release();
+		}
+		return;
+	}
+
+	if (DEVICE_INTR)
+		return;
+
+  repeat:
+	CLEAR_TIMER();
+	/* Another check here: An interrupt or timer event could have
+	 * happened since the last check!
+	 */
+	if (CURRENT && CURRENT->rq_status == RQ_INACTIVE) {
+		if (!DEVICE_INTR) {
+			ENABLE_IRQ();
+			stdma_release();
+		}
+		return;
+	}
+	if (DEVICE_INTR)
+		return;
+
+	if (!CURRENT) {
+		CLEAR_INTR;
+		ENABLE_IRQ();
+		stdma_release();
+		return;
+	}
+	
+	if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
+		panic(DEVICE_NAME ": request list destroyed");
+	if (CURRENT->bh) {
+		if (!CURRENT->bh && !buffer_locked(CURRENT->bh))
+			panic(DEVICE_NAME ": block not locked");
+	}
+
+	dev = MINOR(CURRENT->rq_dev);
+	block = CURRENT->sector;
+	if (DEVICE_NR(dev) >= NDevices ||
+		block+CURRENT->nr_sectors >= acsi_part[dev].nr_sects) {
+#ifdef DEBUG
+		printk( "ad%c: attempted access for blocks %d...%ld past end of device at block %ld.\n",
+		       DEVICE_NR(dev)+'a',
+		       block, block + CURRENT->nr_sectors - 1,
+		       acsi_part[dev].nr_sects);
+#endif
+		end_request(0);
+		goto repeat;
+	}
+	if (acsi_info[DEVICE_NR(dev)].changed) {
+		printk( KERN_NOTICE "ad%c: request denied because cartridge has "
+				"been changed.\n", DEVICE_NR(dev)+'a' );
+		end_request(0);
+		goto repeat;
+	}
+	
+	block += acsi_part[dev].start_sect;
+	target = acsi_info[DEVICE_NR(dev)].target;
+	lun    = acsi_info[DEVICE_NR(dev)].lun;
+
+	/* Find out how many sectors should be transferred from/to
+	 * consecutive buffers and thus can be done with a single command.
+	 */
+	buffer      = CURRENT->buffer;
+	pbuffer     = VTOP(buffer);
+	nsect       = CURRENT->current_nr_sectors;
+	CurrentNReq = 1;
+
+	if ((bh = CURRENT->bh) && bh != CURRENT->bhtail) {
+		if (!STRAM_ADDR(pbuffer)) {
+			/* If transfer is done via the ACSI buffer anyway, we can
+			 * assemble as much bh's as fit in the buffer.
+			 */
+			while( (bh = bh->b_reqnext) ) {
+				if (nsect + (bh->b_size>>9) > ACSI_BUFFER_SECTORS) break;
+				nsect += bh->b_size >> 9;
+				++CurrentNReq;
+				if (bh == CURRENT->bhtail) break;
+			}
+			buffer = acsi_buffer;
+			pbuffer = phys_acsi_buffer;
+		}
+		else {
+			unsigned long pendadr, pnewadr;
+			pendadr = pbuffer + nsect*512;
+			while( (bh = bh->b_reqnext) ) {
+				pnewadr = VTOP(bh->b_data);
+				if (!STRAM_ADDR(pnewadr) || pendadr != pnewadr) break;
+				nsect += bh->b_size >> 9;
+				pendadr = pnewadr + bh->b_size;
+				++CurrentNReq;
+				if (bh == CURRENT->bhtail) break;
+			}
+		}
+	}
+	else {
+		if (!STRAM_ADDR(pbuffer)) {
+			buffer = acsi_buffer;
+			pbuffer = phys_acsi_buffer;
+			if (nsect > ACSI_BUFFER_SECTORS)
+				nsect = ACSI_BUFFER_SECTORS;
+		}
+	}
+	CurrentBuffer = buffer;
+	CurrentNSect  = nsect;
+	
+	if (CURRENT->cmd == WRITE) {
+		CMDSET_TARG_LUN( write_cmd, target, lun );
+		CMDSET_BLOCK( write_cmd, block );
+		CMDSET_LEN( write_cmd, nsect );
+		if (buffer == acsi_buffer)
+			copy_to_acsibuffer();
+		dma_cache_maintenance( pbuffer, nsect*512, 1 );
+		SET_INTR(write_intr);
+		if (!acsicmd_dma( write_cmd, buffer, nsect, 1, 1)) {
+			CLEAR_INTR;
+			printk( KERN_ERR "ACSI (write): Timeout in command block\n" );
+			bad_rw_intr();
+			goto repeat;
+		}
+		SET_TIMER();
+		return;
+	}
+	if (CURRENT->cmd == READ) {
+		CMDSET_TARG_LUN( read_cmd, target, lun );
+		CMDSET_BLOCK( read_cmd, block );
+		CMDSET_LEN( read_cmd, nsect );
+		SET_INTR(read_intr);
+		if (!acsicmd_dma( read_cmd, buffer, nsect, 0, 1)) {
+			CLEAR_INTR;
+			printk( KERN_ERR "ACSI (read): Timeout in command block\n" );
+			bad_rw_intr();
+			goto repeat;
+		}
+		SET_TIMER();
+		return;
+	}
+	panic("unknown ACSI command");
+}
+
+
+
+/***********************************************************************
+ *
+ *  Misc functions: ioctl, open, release, check_change, ...
+ *
+ ***********************************************************************/
+
+
+static int acsi_ioctl( struct inode *inode, struct file *file,
+					   unsigned int cmd, unsigned long arg )
+{	int dev, err;
+
+	if (!inode)
+		return -EINVAL;
+	dev = DEVICE_NR(MINOR(inode->i_rdev));
+	if (dev >= NDevices)
+		return -EINVAL;
+	switch (cmd) {
+		/* I left out the GETGEO cmd; This doesn't make much sense for
+		 * ACSI disks...
+		 */
+	  case BLKGETSIZE:   /* Return device size */
+		if (!arg)  return -EINVAL;
+		err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+		if (err)
+			return err;
+		put_fs_long(acsi_part[MINOR(inode->i_rdev)].nr_sects,
+					(long *) arg);
+		return 0;
+	  case BLKFLSBUF:
+		if(!suser())  return -EACCES;
+		if(!inode->i_rdev) return -EINVAL;
+		fsync_dev(inode->i_rdev);
+		invalidate_buffers(inode->i_rdev);
+		return 0;
+
+	  case BLKRRPART: /* Re-read partition tables */
+		return revalidate_acsidisk(inode->i_rdev, 1);
+	  RO_IOCTLS(inode->i_rdev,arg);
+	  default:
+		return -EINVAL;
+	}
+}
+
+
+/*
+ * Open a device, check for read-only and lock the medium if it is
+ * removable.
+ *
+ * Changes by Martin Rogge, 9th Aug 1995:
+ * Check whether check_disk_change (and therefore revalidate_acsidisk)
+ * was successful. They fail when there is no medium in the drive.
+ *
+ * The problem of media being changed during an operation can be 
+ * ignored because of the prevent_removal code.
+ *
+ * Added check for the validity of the device number.
+ *
+ */
+
+static int acsi_open( struct inode * inode, struct file * filp )
+{
+	int  device;
+	struct acsi_info_struct *aip;
+
+	device = DEVICE_NR(MINOR(inode->i_rdev));
+	if (device >= NDevices)
+		return -ENXIO;
+	aip = &acsi_info[device];
+	while (busy[device])
+		sleep_on(&busy_wait);
+
+	if (access_count[device] == 0 && aip->removable) {
+#if 0
+		aip->changed = 1;	/* safety first */
+#endif
+		check_disk_change( inode->i_rdev );
+		if (aip->changed)	/* revalidate was not successful (no medium) */
+			return -ENXIO;
+		acsi_prevent_removal(device, 1);
+	}
+	access_count[device]++;
+
+	if (filp && filp->f_mode) {
+		check_disk_change( inode->i_rdev );
+		if (filp->f_mode & 2) {
+			if (aip->read_only) {
+				acsi_release( inode, filp );
+				return -EROFS;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
+ */
+
+static void acsi_release( struct inode * inode, struct file * file )
+{
+	int device;
+
+	sync_dev(inode->i_rdev);
+
+	device = DEVICE_NR(MINOR(inode->i_rdev));
+	if (--access_count[device] == 0 && acsi_info[device].removable)
+		acsi_prevent_removal(device, 0);
+}
+
+/*
+ * Prevent or allow a media change for removable devices.
+ */
+
+static void acsi_prevent_removal(int device, int flag)
+{
+	stdma_lock( NULL, NULL );
+	
+	CMDSET_TARG_LUN(pa_med_rem_cmd, acsi_info[device].target,
+			acsi_info[device].lun);
+	CMDSET_LEN( pa_med_rem_cmd, flag );
+	
+	if (acsicmd_nodma(pa_med_rem_cmd, 0) && acsi_wait_for_IRQ(3*HZ))
+		acsi_getstatus();
+	/* Do not report errors -- some devices may not know this command. */
+
+	ENABLE_IRQ();
+	stdma_release();
+}
+
+static int acsi_media_change (dev_t dev)
+{
+	int device = DEVICE_NR(MINOR(dev));
+	struct acsi_info_struct *aip;
+
+	aip = &acsi_info[device];
+	if (!aip->removable) 
+		return 0;
+
+	if (aip->changed)
+		/* We can be sure that the medium has been changed -- REQUEST
+		 * SENSE has reported this earlier.
+		 */
+		return 1;
+
+	/* If the flag isn't set, make a test by reading block 0.
+	 * If errors happen, it seems to be better to say "changed"...
+	 */
+	stdma_lock( NULL, NULL );
+	CMDSET_TARG_LUN(read_cmd, aip->target, aip->lun);
+	CMDSET_BLOCK( read_cmd, 0 );
+	CMDSET_LEN( read_cmd, 1 );
+	if (acsicmd_dma(read_cmd, acsi_buffer, 1, 0, 0) &&
+	    acsi_wait_for_IRQ(3*HZ)) {
+		if (acsi_getstatus()) {
+			if (acsi_reqsense(acsi_buffer, aip->target, aip->lun)) {
+				if (CARTRCH_STAT(device, acsi_buffer))
+					aip->changed = 1;
+			}
+			else {
+				printk( KERN_ERR "ad%c: REQUEST SENSE failed in test for "
+				       "medium change; assuming a change\n", device + 'a' );
+				aip->changed = 1;
+			}
+		}
+	}
+	else {
+		printk( KERN_ERR "ad%c: Test for medium changed timed out; "
+				"assuming a change\n", device + 'a');
+		aip->changed = 1;
+	}
+	ENABLE_IRQ();
+	stdma_release();
+
+	/* Now, after reading a block, the changed status is surely valid. */
+	return aip->changed;
+}
+
+
+static int acsi_change_blk_size( int target, int lun)
+
+{	int i;
+
+	for (i=0; i<12; i++)
+		acsi_buffer[i] = 0;
+
+	acsi_buffer[3] = 8;
+	acsi_buffer[10] = 2;
+	CMDSET_TARG_LUN( modeselect_cmd, target, lun);
+
+	if (!acsicmd_dma( modeselect_cmd, acsi_buffer, 1,1,0) ||
+		!acsi_wait_for_IRQ( 3*HZ ) ||
+		acsi_getstatus() != 0 ) {
+		return(0);
+	}
+	return(1);
+}
+
+
+static int acsi_mode_sense( int target, int lun, SENSE_DATA *sd )
+
+{
+	int page;
+
+	CMDSET_TARG_LUN( modesense_cmd, target, lun );
+	for (page=0; page<4; page++) {
+		modesense_cmd[2] = page;
+		if (!acsicmd_dma( modesense_cmd, acsi_buffer, 1, 0, 0 ) ||
+		    !acsi_wait_for_IRQ( 3*HZ ) ||
+		    acsi_getstatus())
+			continue;
+
+		/* read twice to jump over the second 16-byte border! */
+		udelay(300);
+		if (acsi_wait_for_noIRQ( 20 ) &&
+		    acsicmd_nodma( modesense_cmd, 0 ) &&
+		    acsi_wait_for_IRQ( 3*HZ ) &&
+		    acsi_getstatus() == 0);
+			break;
+	}
+	if (page == 4) {
+		return(0);
+	}
+
+	dma_cache_maintenance( phys_acsi_buffer, sizeof(SENSE_DATA), 0 );
+	*sd = *(SENSE_DATA *)acsi_buffer;
+
+	/* Validity check, depending on type of data */
+	
+	switch( SENSE_TYPE(*sd) ) {
+
+	  case SENSE_TYPE_ATARI:
+		if (CAPACITY(*sd) == 0)
+			goto invalid_sense;
+		break;
+
+	  case SENSE_TYPE_SCSI:
+		if (sd->scsi.descriptor_size != 8)
+			goto invalid_sense;
+		break;
+
+	  case SENSE_TYPE_UNKNOWN:
+
+		printk( KERN_ERR "ACSI target %d, lun %d: Cannot interpret "
+				"sense data\n", target, lun ); 
+		
+	  invalid_sense:
+
+#ifdef DEBUG
+		{	int i;
+		printk( "Mode sense data for ACSI target %d, lun %d seem not valid:",
+				target, lun );
+		for( i = 0; i < sizeof(SENSE_DATA); ++i )
+			printk( "%02x ", (unsigned char)acsi_buffer[i] );
+		printk( "\n" );
+		}
+#endif
+		return( 0 );
+	}
+		
+	return( 1 );
+}
+
+
+
+/*******************************************************************
+ *
+ *  Initialization
+ *
+ ********************************************************************/
+
+
+static struct gendisk acsi_gendisk = {
+	MAJOR_NR,			/* Major number */	
+	"ad",				/* Major name */
+	4,					/* Bits to shift to get real from partition */
+	1 << 4,				/* Number of partitions per real */
+	MAX_DEV,			/* maximum number of real */
+	acsi_geninit,		/* init function */
+	acsi_part,			/* hd struct */
+	acsi_sizes,			/* block sizes */
+	0,					/* number */
+	(void *)acsi_info,	/* internal */
+	NULL				/* next */
+};
+	
+#define MAX_SCSI_DEVICE_CODE 10
+
+static const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
+{
+ "Direct-Access    ",
+ "Sequential-Access",
+ "Printer          ",
+ "Processor        ",
+ "WORM             ",
+ "CD-ROM           ",
+ "Scanner          ",
+ "Optical Device   ",
+ "Medium Changer   ",
+ "Communications   "
+};
+
+static void print_inquiry(unsigned char *data)
+{
+	int i;
+
+	printk("  Vendor: ");
+	for (i = 8; i < 16; i++)
+		{
+	        if (data[i] >= 0x20 && i < data[4] + 5)
+			printk("%c", data[i]);
+		else
+			printk(" ");
+		}
+
+	printk("  Model: ");
+	for (i = 16; i < 32; i++)
+		{
+	        if (data[i] >= 0x20 && i < data[4] + 5)
+			printk("%c", data[i]);
+		else
+			printk(" ");
+		}
+
+	printk("  Rev: ");
+	for (i = 32; i < 36; i++)
+		{
+	        if (data[i] >= 0x20 && i < data[4] + 5)
+			printk("%c", data[i]);
+		else
+			printk(" ");
+		}
+
+	printk("\n");
+
+	i = data[0] & 0x1f;
+
+	printk("  Type:   %s ", (i < MAX_SCSI_DEVICE_CODE
+				 ? scsi_device_types[i]
+				 : "Unknown          "));
+	printk("                 ANSI SCSI revision: %02x", data[2] & 0x07);
+	if ((data[2] & 0x07) == 1 && (data[3] & 0x0f) == 1)
+	  printk(" CCS\n");
+	else
+	  printk("\n");
+}
+
+
+/* 
+ * Changes by Martin Rogge, 9th Aug 1995: 
+ * acsi_devinit has been taken out of acsi_geninit, because it needs 
+ * to be called from revalidate_acsidisk. The result of request sense 
+ * is now checked for DRIVE NOT READY.
+ *
+ * The structure *aip is only valid when acsi_devinit returns 
+ * DEV_SUPPORTED. 
+ *
+ */
+	
+#define DEV_NONE	0
+#define DEV_UNKNOWN	1
+#define DEV_SUPPORTED	2
+#define DEV_SLM		3
+
+static int acsi_devinit(struct acsi_info_struct *aip)
+{
+	int status, got_inquiry;
+	SENSE_DATA sense;
+	unsigned char reqsense, extsense;
+
+	/*****************************************************************/
+	/* Do a TEST UNIT READY command to test the presence of a device */
+	/*****************************************************************/
+
+	CMDSET_TARG_LUN(tur_cmd, aip->target, aip->lun);
+	if (!acsicmd_nodma(tur_cmd, 0)) {
+		/* timed out -> no device here */
+#ifdef DEBUG_DETECT
+		printk("target %d lun %d: timeout\n", aip->target, aip->lun);
+#endif
+		return DEV_NONE;
+	}
+		
+	/*************************/
+	/* Read the ACSI status. */
+	/*************************/
+
+	status = acsi_getstatus();
+	if (status) {
+		if (status == 0x12) {
+			/* The SLM printer should be the only device that
+			 * responds with the error code in the status byte. In
+			 * correct status bytes, bit 4 is never set.
+			 */
+			printk( "Detected SLM printer at id %d lun %d\n",
+			       aip->target, aip->lun);
+			return DEV_SLM;
+		}
+		/* ignore CHECK CONDITION, since some devices send a
+		   UNIT ATTENTION */
+		if ((status & 0x1e) != 0x2) {
+#ifdef DEBUG_DETECT
+			printk("target %d lun %d: status %d\n",
+			       aip->target, aip->lun, status);
+#endif
+			return DEV_UNKNOWN;
+		}
+	}
+
+	/*******************************/
+	/* Do a REQUEST SENSE command. */
+	/*******************************/
+
+	if (!acsi_reqsense(acsi_buffer, aip->target, aip->lun)) {
+		printk("acsi_reqsense failed\n");
+		acsi_buffer[0] = 0;
+		acsi_buffer[2] = UNIT_ATTENTION;
+	}
+	reqsense = acsi_buffer[0];
+	extsense = acsi_buffer[2] & 0xf;
+	if (status) {
+		if ((reqsense & 0x70) == 0x70) {	/* extended sense */
+			if (extsense != UNIT_ATTENTION &&
+			    extsense != NOT_READY) {
+#ifdef DEBUG_DETECT
+				printk("target %d lun %d: extended sense %d\n",
+				       aip->target, aip->lun, extsense);
+#endif
+				return DEV_UNKNOWN;
+			}
+		}
+		else {
+			if (reqsense & 0x7f) {
+#ifdef DEBUG_DETECT
+				printk("target %d lun %d: sense %d\n",
+				       aip->target, aip->lun, reqsense);
+#endif
+				return DEV_UNKNOWN;
+			}
+		}
+	}
+	else 
+		if (reqsense == 0x4) {	/* SH204 Bug workaround */
+#ifdef DEBUG_DETECT
+			printk("target %d lun %d status=0 sense=4\n",
+			       aip->target, aip->lun);
+#endif
+			return DEV_UNKNOWN;
+		}
+
+	/***********************************************************/
+	/* Do an INQUIRY command to get more infos on this device. */
+	/***********************************************************/
+
+	/* Assume default values */
+	aip->removable = 1;
+	aip->read_only = 0;
+	aip->old_atari_disk = 0;
+	aip->changed = (extsense == NOT_READY);	/* medium inserted? */
+	aip->size = DEFAULT_SIZE;
+	got_inquiry = 0;
+	/* Fake inquiry result for old atari disks */
+	memcpy(acsi_buffer, "\000\000\001\000    Adaptec 40xx"
+	       "                    ", 40);
+	CMDSET_TARG_LUN(inquiry_cmd, aip->target, aip->lun);
+	if (acsicmd_dma(inquiry_cmd, acsi_buffer, 1, 0, 0) &&
+	    acsi_getstatus() == 0) {
+		acsicmd_nodma(inquiry_cmd, 0);
+		acsi_getstatus();
+		dma_cache_maintenance( phys_acsi_buffer, 256, 0 );
+		got_inquiry = 1;
+		aip->removable = !!(acsi_buffer[1] & 0x80);
+	}
+	if (aip->type == NONE)	/* only at boot time */
+		print_inquiry(acsi_buffer);
+	switch(acsi_buffer[0]) {
+	  case TYPE_DISK:
+		aip->type = HARDDISK;
+		break;
+	  case TYPE_ROM:
+		aip->type = CDROM;
+		aip->read_only = 1;
+		break;
+	  default:
+		return DEV_UNKNOWN;
+	}
+	/****************************/
+	/* Do a MODE SENSE command. */
+	/****************************/
+
+	if (!acsi_mode_sense(aip->target, aip->lun, &sense)) {
+		printk( KERN_WARNING "No mode sense data.\n" );
+		return DEV_UNKNOWN;
+	}
+	if ((SECTOR_SIZE(sense) != 512) &&
+	    ((aip->type != CDROM) ||
+	     !acsi_change_blk_size(aip->target, aip->lun) ||
+	     !acsi_mode_sense(aip->target, aip->lun, &sense) ||
+	     (SECTOR_SIZE(sense) != 512))) {
+		printk( KERN_WARNING "Sector size != 512 not supported.\n" );
+		return DEV_UNKNOWN;
+	}
+	/* There are disks out there that claim to have 0 sectors... */
+	if (CAPACITY(sense))
+		aip->size = CAPACITY(sense);	/* else keep DEFAULT_SIZE */
+	if (!got_inquiry && SENSE_TYPE(sense) == SENSE_TYPE_ATARI) {
+		/* If INQUIRY failed and the sense data suggest an old
+		 * Atari disk (SH20x, Megafile), the disk is not removable
+		 */
+		aip->removable = 0;
+		aip->old_atari_disk = 1;
+	}
+	
+	/******************/
+	/* We've done it. */
+	/******************/
+	
+	return DEV_SUPPORTED;
+}
+
+static struct symbol_table acsi_symbol_table =
+{
+#include <linux/symtab_begin.h>
+
+	X(acsi_delay_start),
+	X(acsi_delay_end),
+	X(acsi_wait_for_IRQ),
+	X(acsi_wait_for_noIRQ),
+	X(acsicmd_nodma),
+	X(acsi_getstatus),
+	X(acsi_buffer),
+	X(phys_acsi_buffer),
+#ifdef CONFIG_ATARI_SLM_MODULE
+	X(acsi_extstatus),
+	X(acsi_end_extstatus),
+	X(acsi_extcmd),
+#endif
+
+#include <linux/symtab_end.h>
+};
+		
+static void acsi_geninit( struct gendisk *gd )
+{
+	int i, target, lun;
+	struct acsi_info_struct *aip;
+	
+	printk ("Probing ACSI devices:\n");
+	NDevices = 0;
+	stdma_lock(NULL, NULL);
+
+	for (target = 0; target < 8 && NDevices < MAX_DEV; ++target) {
+		lun = 0;
+		do {
+			aip = &acsi_info[NDevices];
+			aip->type = NONE;
+			aip->target = target;
+			aip->lun = lun;
+			i = acsi_devinit(aip);
+			switch (i) {
+			  case DEV_SUPPORTED:
+				printk("Detected ");
+				switch (aip->type) {
+				  case HARDDISK:
+					printk("disk");
+					break;
+				  case CDROM:
+					printk("cdrom");
+					break;
+				  default:
+				}
+				printk(" ad%c at id %d lun %d ",
+				       'a' + NDevices, target, lun);
+				if (aip->removable) 
+					printk("(removable) ");
+				if (aip->read_only) 
+					printk("(read-only) ");
+				if (aip->size == DEFAULT_SIZE)
+					printk(" unkown size, using default ");
+				printk("%ld MByte\n",
+				       (aip->size*512+1024*1024/2)/(1024*1024));
+				NDevices++;
+				break;
+			  case DEV_SLM:
+#ifdef CONFIG_ATARI_SLM
+				attach_slm( target, lun );
+				break;
+#endif
+			  case DEV_UNKNOWN:
+				printk("Detected unsupported device at id %d lun %d\n",
+				       target, lun);
+				break;
+			}
+		}
+#ifdef CONFIG_ACSI_MULTI_LUN
+		while (i != DEV_NONE && ++lun < MAX_LUN);
+#else
+		while (0);
+#endif
+	}
+
+	/* reenable interrupt */
+	ENABLE_IRQ();
+	stdma_release();
+
+#ifndef CONFIG_ATARI_SLM
+	printk( "Found %d ACSI device(s) total.\n", NDevices );
+#else
+	printk( "Found %d ACSI device(s) and %d SLM printer(s) total.\n",
+			NDevices, N_SLM_Printers );
+#endif
+					 
+	for( i = 0; i < NDevices; ++i ) {
+		acsi_part[i<<4].start_sect = 0;
+		acsi_part[i<<4].nr_sects = acsi_info[i].size;
+	}
+	acsi_gendisk.nr_real = NDevices;
+	for( i = 0; i < (MAX_DEV << 4); i++ )
+		acsi_blocksizes[i] = 1024;
+	blksize_size[MAJOR_NR] = acsi_blocksizes;
+	register_symtab (&acsi_symbol_table);
+}
+
+
+static struct file_operations acsi_fops = {
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,	/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	acsi_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	acsi_open,		/* open */
+	acsi_release,	/* release */
+	block_fsync,		/* fsync */
+	NULL,			/* fasync */
+	acsi_media_change,	/* media_change */
+	acsi_revalidate,	/* revalidate */
+};
+
+
+int acsi_init( void )
+
+{
+	if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ACSI))
+		return 0;
+
+	if (register_blkdev( MAJOR_NR, "ad", &acsi_fops )) {
+		printk( KERN_ERR "Unable to get major %d for ACSI\n", MAJOR_NR );
+		return -EBUSY;
+	}
+
+	if (!(acsi_buffer = kmalloc((ATARIHW_PRESENT(EXTD_DMA) ?
+				 ACSI_BUFFER_MINSIZE : ACSI_BUFFER_SIZE),
+				 GFP_KERNEL | GFP_DMA))) {
+		printk( KERN_ERR "Unable to get ACSI ST-Ram buffer.\n" );
+		return -ENOMEM;
+	}
+	phys_acsi_buffer = VTOP( acsi_buffer );
+	STramMask = ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000 : 0xff000000;
+	
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 8;		/* 8 sector (4kB) read-ahead */
+	acsi_gendisk.next = gendisk_head;
+	gendisk_head = &acsi_gendisk;
+
+#ifdef CONFIG_ATARI_SLM
+	return( slm_init() );
+#else
+	return 0;
+#endif
+}
+
+
+#define DEVICE_BUSY busy[device]
+#define USAGE access_count[device]
+#define GENDISK_STRUCT acsi_gendisk
+
+/*
+ * This routine is called to flush all partitions and partition tables
+ * for a changed scsi disk, and then re-read the new partition table.
+ * If we are revalidating a disk because of a media change, then we
+ * enter with usage == 0.  If we are using an ioctl, we automatically have
+ * usage == 1 (we need an open channel to use an ioctl :-), so this
+ * is our limit.
+ *
+ * Changes by Martin Rogge, 9th Aug 1995: 
+ * got cd-roms to work by calling acsi_devinit. There are only two problems:
+ * First, if there is no medium inserted, the status will remain "changed".
+ * That is no problem at all, but our design of three-valued logic (medium
+ * changed, medium not changed, no medium inserted).
+ * Secondly the check could fail completely and the drive could deliver
+ * nonsensical data, which could mess up the acsi_info[] structure. In
+ * that case we try to make the entry safe.
+ *
+ */
+
+static int revalidate_acsidisk( int dev, int maxusage )
+{
+	int device, major;
+	struct gendisk * gdev;
+	int max_p, start, i;
+	struct acsi_info_struct *aip;
+	
+	device = DEVICE_NR(MINOR(dev));
+	aip = &acsi_info[device];
+	gdev = &GENDISK_STRUCT;
+
+	cli();
+	if (DEVICE_BUSY || USAGE > maxusage) {
+		sti();
+		return -EBUSY;
+	};
+	DEVICE_BUSY = 1;
+	sti();
+
+	max_p = gdev->max_p;
+	start = device << gdev->minor_shift;
+	major = MAJOR_NR << 8;
+
+	for( i = max_p - 1; i >= 0 ; i-- ) {
+		sync_dev( major | start | i );
+		invalidate_inodes( major | start | i );
+		invalidate_buffers( major | start | i );
+		gdev->part[start+i].start_sect = 0;
+		gdev->part[start+i].nr_sects = 0;
+	};
+
+	stdma_lock( NULL, NULL );
+
+	if (acsi_devinit(aip) != DEV_SUPPORTED) {
+		printk( KERN_ERR "ACSI: revalidate failed for target %d lun %d\n",
+		       aip->target, aip->lun);
+		aip->size = 0;
+		aip->read_only = 1;
+		aip->removable = 1;
+		aip->changed = 1; /* next acsi_open will try again... */
+	}
+
+	ENABLE_IRQ();
+	stdma_release();
+	
+	gdev->part[start].nr_sects = aip->size;
+	if (aip->type == HARDDISK && aip->size > 0)
+		resetup_one_dev(gdev, device);
+
+	DEVICE_BUSY = 0;
+	wake_up(&busy_wait);
+	return 0;
+}
+
+
+static int acsi_revalidate (dev_t dev)
+{
+  return revalidate_acsidisk (dev, 0);
+}
diff -urN pc-2.0.21/drivers/block/acsi_slm.c linux-2.0.21/drivers/block/acsi_slm.c
--- pc-2.0.21/drivers/block/acsi_slm.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/block/acsi_slm.c	Sat Aug 24 14:33:09 1996
@@ -0,0 +1,1004 @@
+/*
+ * acsi_slm.c -- Device driver for the Atari SLM laser printer
+ *
+ * Copyright 1995 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ *
+ * 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.
+ * 
+ */
+
+/*
+
+Notes:
+
+The major number for SLM printers is 28 (like ACSI), but as a character
+device, not block device. The minor number is the number of the printer (if
+you have more than one SLM; currently max. 2 (#define-constant) SLMs are
+supported). The device can be opened for reading and writing. If reading it,
+you get some status infos (MODE SENSE data). Writing mode is used for the data
+to be printed. Some ioctls allow to get the printer status and to tune printer
+modes and some internal variables.
+
+A special problem of the SLM driver is the timing and thus the buffering of
+the print data. The problem is that all the data for one page must be present
+in memory when printing starts, else --when swapping occurs-- the timing could
+not be guaranteed. There are several ways to assure this:
+
+ 1) Reserve a buffer of 1196k (maximum page size) statically by
+    atari_stram_alloc(). The data are collected there until they're complete,
+	and then printing starts. Since the buffer is reserved, no further
+	considerations about memory and swapping are needed. So this is the
+	simplest method, but it needs a lot of memory for just the SLM.
+
+    An striking advantage of this method is (supposed the SLM_CONT_CNT_REPROG
+	method works, see there), that there are no timing problems with the DMA
+	anymore.
+	
+ 2) The other method would be to reserve the buffer dynamically each time
+    printing is required. I could think of looking at mem_map where the
+	largest unallocted ST-RAM area is, taking the area, and then extending it
+	by swapping out the neighbored pages, until the needed size is reached.
+	This requires some mm hacking, but seems possible. The only obstacle could
+	be pages that cannot be swapped out (reserved pages)...
+
+ 3) Another possibility would be to leave the real data in user space and to
+    work with two dribble buffers of about 32k in the driver: While the one
+	buffer is DMAed to the SLM, the other can be filled with new data. But
+	to keep the timing, that requires that the user data remain in memory and
+	are not swapped out. Requires mm hacking, too, but maybe not so bad as
+	method 2).
+
+*/
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+#include <asm/setup.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atari_acsi.h>
+#include <asm/atari_stdma.h>
+#include <asm/atari_SLM.h>
+
+
+#undef	DEBUG
+
+/* Define this if the page data are continuous in physical memory. That
+ * requires less reprogramming of the ST-DMA */
+#define	SLM_CONTINUOUS_DMA
+
+/* Use continuous reprogramming of the ST-DMA counter register. This is
+ * --strictly speaking-- not allowed, Atari recommends not to look at the
+ * counter register while a DMA is going on. But I don't know if that applies
+ * only for reading the register, or also writing to it. Writing only works
+ * fine for me... The advantage is that the timing becomes absolutely
+ * uncritical: Just update each, say 200ms, the counter reg to its maximum,
+ * and the DMA will work until the status byte interrupt occurs.
+ */
+#define	SLM_CONT_CNT_REPROG
+
+#define MAJOR_NR ACSI_MAJOR
+
+#define CMDSET_TARG_LUN(cmd,targ,lun)			\
+    do {										\
+		cmd[0] = (cmd[0] & ~0xe0) | (targ)<<5;	\
+		cmd[1] = (cmd[1] & ~0xe0) | (lun)<<5;	\
+	} while(0)
+
+#define	START_TIMER(to)							\
+    do {										\
+        del_timer( &slm_timer );				\
+        slm_timer.expires = jiffies + (to);			\
+        add_timer( &slm_timer );				\
+	} while(0)
+
+#define	STOP_TIMER()							\
+    do {										\
+        del_timer( &slm_timer );				\
+	} while(0)
+
+
+static char slmreqsense_cmd[6] = { 0x03, 0, 0, 0, 0, 0 };
+static char slmprint_cmd[6]    = { 0x0a, 0, 0, 0, 0, 0 };
+static char slminquiry_cmd[6]  = { 0x12, 0, 0, 0, 0, 0x80 };
+static char slmmsense_cmd[6]   = { 0x1a, 0, 0, 0, 255, 0 };
+static char slmmselect_cmd[6]  = { 0x15, 0, 0, 0, 0, 0 };
+
+
+#define	MAX_SLM		2
+
+static struct slm {
+	unsigned	target;			/* target number */
+	unsigned	lun;			/* LUN in target controller */
+	unsigned	wbusy : 1;		/* output part busy */
+	unsigned	rbusy : 1;		/* status part busy */
+} slm_info[MAX_SLM];
+
+int N_SLM_Printers = 0;
+
+/* printer buffer */
+static unsigned char	*SLMBuffer;	/* start of buffer */
+static unsigned char	*BufferP;	/* current position in buffer */
+static int				BufferSize;	/* length of buffer for page size */
+
+typedef enum { IDLE, FILLING, PRINTING } SLMSTATE;
+static SLMSTATE			SLMState;
+static int				SLMBufOwner;	/* SLM# currently using the buffer */
+
+/* DMA variables */
+#ifndef SLM_CONT_CNT_REPROG
+static unsigned long	SLMCurAddr;		/* current base addr of DMA chunk */
+static unsigned long	SLMEndAddr;		/* expected end addr */
+static unsigned long	SLMSliceSize;	/* size of one DMA chunk */
+#endif
+static int				SLMError;
+
+/* wait queues */
+static struct wait_queue *slm_wait;		/* waiting for buffer */
+static struct wait_queue *print_wait;	/* waiting for printing finished */
+
+/* status codes */
+#define	SLMSTAT_OK		0x00
+#define	SLMSTAT_ORNERY	0x02
+#define	SLMSTAT_TONER	0x03
+#define	SLMSTAT_WARMUP	0x04
+#define	SLMSTAT_PAPER	0x05
+#define	SLMSTAT_DRUM	0x06
+#define	SLMSTAT_INJAM	0x07
+#define	SLMSTAT_THRJAM	0x08
+#define	SLMSTAT_OUTJAM	0x09
+#define	SLMSTAT_COVER	0x0a
+#define	SLMSTAT_FUSER	0x0b
+#define	SLMSTAT_IMAGER	0x0c
+#define	SLMSTAT_MOTOR	0x0d
+#define	SLMSTAT_VIDEO	0x0e
+#define	SLMSTAT_SYSTO	0x10
+#define	SLMSTAT_OPCODE	0x12
+#define	SLMSTAT_DEVNUM	0x15
+#define	SLMSTAT_PARAM	0x1a
+#define	SLMSTAT_ACSITO	0x1b	/* driver defined */
+#define	SLMSTAT_NOTALL	0x1c	/* driver defined */
+
+static char *SLMErrors[] = {
+	/* 0x00 */	"OK and ready",
+	/* 0x01 */	NULL,
+	/* 0x02 */	"ornery printer",
+	/* 0x03 */	"toner empty",
+	/* 0x04 */	"warming up",
+	/* 0x05 */	"paper empty",
+	/* 0x06 */	"drum empty",
+	/* 0x07 */	"input jam",
+	/* 0x08 */	"through jam",
+	/* 0x09 */	"output jam",
+	/* 0x0a */	"cover open",
+	/* 0x0b */	"fuser malfunction",
+	/* 0x0c */	"imager malfunction",
+	/* 0x0d */	"motor malfunction",
+	/* 0x0e */	"video malfunction",
+	/* 0x0f */	NULL,
+	/* 0x10 */	"printer system timeout",
+	/* 0x11 */	NULL,
+	/* 0x12 */	"invalid operation code",
+	/* 0x13 */	NULL,
+	/* 0x14 */	NULL,
+	/* 0x15 */	"invalid device number",
+	/* 0x16 */	NULL,
+	/* 0x17 */	NULL,
+	/* 0x18 */	NULL,
+	/* 0x19 */	NULL,
+	/* 0x1a */	"invalid parameter list",
+	/* 0x1b */	"ACSI timeout",
+	/* 0x1c */	"not all printed"
+};
+
+#define	N_ERRORS	(sizeof(SLMErrors)/sizeof(*SLMErrors))
+
+/* real (driver caused) error? */
+#define	IS_REAL_ERROR(x)	(x > 0x10)
+
+
+static struct {
+	char	*name;
+	int 	w, h;
+} StdPageSize[] = {
+	{ "Letter", 2400, 3180 },
+	{ "Legal",  2400, 4080 },
+	{ "A4",     2336, 3386 },
+	{ "B5",     2016, 2914 }
+};
+
+#define	N_STD_SIZES		(sizeof(StdPageSize)/sizeof(*StdPageSize))
+
+#define	SLM_BUFFER_SIZE	(2336*3386/8)	/* A4 for now */
+#define	SLM_DMA_AMOUNT	255				/* #sectors to program the DMA for */
+
+#ifdef	SLM_CONTINUOUS_DMA
+# define	SLM_DMA_INT_OFFSET	0		/* DMA goes until seccnt 0, no offs */
+# define	SLM_DMA_END_OFFSET	32		/* 32 Byte ST-DMA FIFO */
+# define	SLM_SLICE_SIZE(w) 	(255*512)
+#else
+# define	SLM_DMA_INT_OFFSET	32		/* 32 Byte ST-DMA FIFO */
+# define	SLM_DMA_END_OFFSET	32		/* 32 Byte ST-DMA FIFO */
+# define	SLM_SLICE_SIZE(w)	((254*512)/(w/8)*(w/8))
+#endif
+
+/* calculate the number of jiffies to wait for 'n' bytes */
+#ifdef SLM_CONT_CNT_REPROG
+#define	DMA_TIME_FOR(n)		50
+#define	DMA_STARTUP_TIME	0
+#else
+#define	DMA_TIME_FOR(n)		(n/1400-1)
+#define	DMA_STARTUP_TIME	650
+#endif
+
+/***************************** Prototypes *****************************/
+
+static char *slm_errstr( int stat );
+static int slm_getstats( char *buffer, int device );
+static int slm_read( struct inode *node, struct file* file, char *buf, int
+                     count );
+static void start_print( int device );
+static void slm_interrupt(int irc, void *data, struct pt_regs *fp);
+static void slm_test_ready( unsigned long dummy );
+static void set_dma_addr( unsigned long paddr );
+static unsigned long get_dma_addr( void );
+static int slm_write( struct inode *node, struct file *file, const char *buf, int
+                      count );
+static int slm_ioctl( struct inode *inode, struct file *file, unsigned int
+                      cmd, unsigned long arg );
+static int slm_open( struct inode *inode, struct file *file );
+static void slm_release( struct inode *inode, struct file *file );
+static int slm_req_sense( int device );
+static int slm_mode_sense( int device, char *buffer, int abs_flag );
+static int slm_mode_select( int device, char *buffer, int len, int
+                            default_flag );
+static int slm_get_pagesize( int device, int *w, int *h );
+
+/************************* End of Prototypes **************************/
+
+
+static struct timer_list slm_timer = { NULL, NULL, 0, 0, slm_test_ready };
+
+static struct file_operations slm_fops = {
+	NULL,			/* lseek - default */
+	slm_read,		/* read - status reading */
+	slm_write,		/* write - printing data write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	slm_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	slm_open,		/* open */
+	slm_release,	/* release */
+	NULL			/* fsync */
+};
+
+
+/* ---------------------------------------------------------------------- */
+/*							   Status Functions							  */
+
+
+static char *slm_errstr( int stat )
+
+{	char *p;
+	static char	str[22];
+
+	stat &= 0x1f;
+	if (stat >= 0 && stat < N_ERRORS && (p = SLMErrors[stat]))
+		return( p );
+	sprintf( str, "unknown status 0x%02x", stat );
+	return( str );
+}
+
+
+static int slm_getstats( char *buffer, int device )
+
+{	int 			len = 0, stat, i, w, h;
+	unsigned char	buf[256];
+	
+	stat = slm_mode_sense( device, buf, 0 );
+	if (IS_REAL_ERROR(stat))
+		return( -EIO );
+	
+#define SHORTDATA(i)		((buf[i] << 8) | buf[i+1])
+#define	BOOLDATA(i,mask)	((buf[i] & mask) ? "on" : "off")
+
+	w = SHORTDATA( 3 );
+	h = SHORTDATA( 1 );
+		
+	len += sprintf( buffer+len, "Status\t\t%s\n",
+					slm_errstr( stat ) );
+	len += sprintf( buffer+len, "Page Size\t%dx%d",
+					w, h );
+
+	for( i = 0; i < N_STD_SIZES; ++i ) {
+		if (w == StdPageSize[i].w && h == StdPageSize[i].h)
+			break;
+	}
+	if (i < N_STD_SIZES)
+		len += sprintf( buffer+len, " (%s)", StdPageSize[i].name );
+	buffer[len++] = '\n';
+
+	len += sprintf( buffer+len, "Top/Left Margin\t%d/%d\n",
+					SHORTDATA( 5 ), SHORTDATA( 7 ) );
+	len += sprintf( buffer+len, "Manual Feed\t%s\n",
+					BOOLDATA( 9, 0x01 ) );
+	len += sprintf( buffer+len, "Input Select\t%d\n",
+					(buf[9] >> 1) & 7 );
+	len += sprintf( buffer+len, "Auto Select\t%s\n",
+					BOOLDATA( 9, 0x10 ) );
+	len += sprintf( buffer+len, "Prefeed Paper\t%s\n",
+					BOOLDATA( 9, 0x20 ) );
+	len += sprintf( buffer+len, "Thick Pixels\t%s\n",
+					BOOLDATA( 9, 0x40 ) );
+	len += sprintf( buffer+len, "H/V Resol.\t%d/%d dpi\n",
+					SHORTDATA( 12 ), SHORTDATA( 10 ) );
+	len += sprintf( buffer+len, "System Timeout\t%d\n",
+					buf[14] );
+	len += sprintf( buffer+len, "Scan Time\t%d\n",
+					SHORTDATA( 15 ) );
+	len += sprintf( buffer+len, "Page Count\t%d\n",
+					SHORTDATA( 17 ) );
+	len += sprintf( buffer+len, "In/Out Cap.\t%d/%d\n",
+					SHORTDATA( 19 ), SHORTDATA( 21 ) );
+	len += sprintf( buffer+len, "Stagger Output\t%s\n",
+					BOOLDATA( 23, 0x01 ) );
+	len += sprintf( buffer+len, "Output Select\t%d\n",
+					(buf[23] >> 1) & 7 );
+	len += sprintf( buffer+len, "Duplex Print\t%s\n",
+					BOOLDATA( 23, 0x10 ) );
+	len += sprintf( buffer+len, "Color Sep.\t%s\n",
+					BOOLDATA( 23, 0x20 ) );
+
+	return( len );
+}
+
+
+static int slm_read( struct inode *node, struct file* file,
+					 char *buf, int count )
+
+{	unsigned long page;
+	int length;
+	int end;
+
+	if (count < 0)
+		return( -EINVAL );
+	if (!(page = __get_free_page( GFP_KERNEL )))
+		return( -ENOMEM );
+	
+	length = slm_getstats( (char *)page, MINOR(node->i_rdev) );
+	if (length < 0) {
+		free_page( page );
+		return( length );
+	}
+	if (file->f_pos >= length) {
+		free_page( page );
+		return( 0 );
+	}
+	if (count + file->f_pos > length)
+		count = length - file->f_pos;
+	end = count + file->f_pos;
+	memcpy_tofs( buf, (char *)page + file->f_pos, count );
+	free_page( page );
+	file->f_pos = end;
+	return( count );
+}
+
+
+/* ---------------------------------------------------------------------- */
+/*								   Printing								  */
+
+
+static void start_print( int device )
+
+{	struct slm *sip = &slm_info[device];
+	unsigned char	*cmd;
+	unsigned long	paddr;
+	int				i;
+	
+	stdma_lock( slm_interrupt, NULL );
+
+	CMDSET_TARG_LUN( slmprint_cmd, sip->target, sip->lun );
+	cmd = slmprint_cmd;
+	paddr = VTOP( SLMBuffer );
+	dma_cache_maintenance( paddr, VTOP(BufferP)-paddr, 1 );
+	DISABLE_IRQ();
+
+	/* Low on A1 */
+	dma_wd.dma_mode_status = 0x88;
+	MFPDELAY();
+
+	/* send the command bytes except the last */
+	for( i = 0; i < 5; ++i ) {
+		DMA_LONG_WRITE( *cmd++, 0x8a );
+		udelay(20);
+		if (!acsi_wait_for_IRQ( HZ/2 )) {
+			SLMError = 1;
+			return; /* timeout */
+		}
+	}
+	/* last command byte */
+	DMA_LONG_WRITE( *cmd++, 0x82 );
+	MFPDELAY();
+	/* set DMA address */
+	set_dma_addr( paddr );
+	/* program DMA for write and select sector counter reg */
+	dma_wd.dma_mode_status = 0x192;
+	MFPDELAY();
+	/* program for 255*512 bytes and start DMA */
+	DMA_LONG_WRITE( SLM_DMA_AMOUNT, 0x112 );
+
+#ifndef SLM_CONT_CNT_REPROG
+	SLMCurAddr = paddr;
+	SLMEndAddr = paddr + SLMSliceSize + SLM_DMA_INT_OFFSET;
+#endif
+	START_TIMER( DMA_STARTUP_TIME + DMA_TIME_FOR( SLMSliceSize ));
+#if !defined(SLM_CONT_CNT_REPROG) && defined(DEBUG)
+	printk( "SLM: CurAddr=%#lx EndAddr=%#lx timer=%ld\n",
+			SLMCurAddr, SLMEndAddr, DMA_TIME_FOR( SLMSliceSize ) );
+#endif
+	
+	ENABLE_IRQ();
+}
+
+
+/* Only called when an error happened or at the end of a page */
+
+static void slm_interrupt(int irc, void *data, struct pt_regs *fp)
+
+{	unsigned long	addr;
+	int				stat;
+	
+	STOP_TIMER();
+	addr = get_dma_addr();
+	stat = acsi_getstatus();
+	SLMError = (stat < 0)             ? SLMSTAT_ACSITO :
+		       (addr < VTOP(BufferP)) ? SLMSTAT_NOTALL :
+									    stat;
+
+	dma_wd.dma_mode_status = 0x80;
+	MFPDELAY();
+#ifdef DEBUG
+	printk( "SLM: interrupt, addr=%#lx, error=%d\n", addr, SLMError );
+#endif
+
+	wake_up( &print_wait );
+	stdma_release();
+	ENABLE_IRQ();
+}
+
+
+static void slm_test_ready( unsigned long dummy )
+
+{
+#ifdef SLM_CONT_CNT_REPROG
+	/* program for 255*512 bytes again */
+	dma_wd.fdc_acces_seccount = SLM_DMA_AMOUNT;
+	START_TIMER( DMA_TIME_FOR(0) );
+#ifdef DEBUG
+	printk( "SLM: reprogramming timer for %d jiffies, addr=%#lx\n",
+			DMA_TIME_FOR(0), get_dma_addr() );
+#endif
+	
+#else /* !SLM_CONT_CNT_REPROG */
+
+	unsigned long	flags, addr;
+	int				d, ti;
+#ifdef DEBUG
+	struct timeval start_tm, end_tm;
+	int			   did_wait = 0;
+#endif
+
+	save_flags(flags);
+	cli();
+	
+	addr = get_dma_addr();
+	if ((d = SLMEndAddr - addr) > 0) {
+		restore_flags(flags);
+		
+		/* slice not yet finished, decide whether to start another timer or to
+		 * busy-wait */
+		ti = DMA_TIME_FOR( d );
+		if (ti > 0) {
+#ifdef DEBUG
+			printk( "SLM: reprogramming timer for %d jiffies, rest %d bytes\n",
+					ti, d );
+#endif
+			START_TIMER( ti );
+			return;
+		}
+		/* wait for desired end address to be reached */
+#ifdef DEBUG
+		do_gettimeofday( &start_tm );
+		did_wait = 1;
+#endif
+		cli();
+		while( get_dma_addr() < SLMEndAddr )
+			barrier();
+	}
+
+	/* slice finished, start next one */
+	SLMCurAddr += SLMSliceSize;
+
+#ifdef SLM_CONTINUOUS_DMA
+	/* program for 255*512 bytes again */
+	dma_wd.fdc_acces_seccount = SLM_DMA_AMOUNT;
+#else
+	/* set DMA address;
+	 * add 2 bytes for the ones in the SLM controller FIFO! */
+	set_dma_addr( SLMCurAddr + 2 );
+	/* toggle DMA to write and select sector counter reg */
+	dma_wd.dma_mode_status = 0x92;
+	MFPDELAY();
+	dma_wd.dma_mode_status = 0x192;
+	MFPDELAY();
+	/* program for 255*512 bytes and start DMA */
+	DMA_LONG_WRITE( SLM_DMA_AMOUNT, 0x112 );
+#endif
+	
+	restore_flags(flags);
+
+#ifdef DEBUG
+	if (did_wait) {
+		int ms;
+		do_gettimeofday( &end_tm );
+		ms = (end_tm.tv_sec*1000000+end_tm.tv_usec) -
+			 (start_tm.tv_sec*1000000+start_tm.tv_usec); 
+		printk( "SLM: did %ld.%ld ms busy waiting for %d bytes\n",
+				ms/1000, ms%1000, d );
+	}
+	else
+		printk( "SLM: didn't wait (!)\n" );
+#endif
+
+	if ((unsigned char *)PTOV( SLMCurAddr + SLMSliceSize ) >= BufferP) {
+		/* will be last slice, no timer necessary */
+#ifdef DEBUG
+		printk( "SLM: CurAddr=%#lx EndAddr=%#lx last slice -> no timer\n",
+				SLMCurAddr, SLMEndAddr );
+#endif
+	}
+	else {
+		/* not last slice */
+		SLMEndAddr = SLMCurAddr + SLMSliceSize + SLM_DMA_INT_OFFSET;
+		START_TIMER( DMA_TIME_FOR( SLMSliceSize ));
+#ifdef DEBUG
+		printk( "SLM: CurAddr=%#lx EndAddr=%#lx timer=%ld\n",
+				SLMCurAddr, SLMEndAddr, DMA_TIME_FOR( SLMSliceSize ) );
+#endif
+	}
+#endif /* SLM_CONT_CNT_REPROG */
+}
+
+
+static void set_dma_addr( unsigned long paddr )
+
+{	unsigned long	flags;
+	
+	save_flags(flags);  
+	cli();
+	dma_wd.dma_lo = (unsigned char)paddr;
+	paddr >>= 8;
+	MFPDELAY();
+	dma_wd.dma_md = (unsigned char)paddr;
+	paddr >>= 8;
+	MFPDELAY();
+	if (ATARIHW_PRESENT( EXTD_DMA ))
+		st_dma_ext_dmahi = (unsigned short)paddr;
+	else
+		dma_wd.dma_hi = (unsigned char)paddr;
+	MFPDELAY();
+	restore_flags(flags);
+}
+
+
+static unsigned long get_dma_addr( void )
+
+{	unsigned long	addr;
+	
+	addr = dma_wd.dma_lo & 0xff;
+	MFPDELAY();
+	addr |= (dma_wd.dma_md & 0xff) << 8;
+	MFPDELAY();
+	addr |= (dma_wd.dma_hi & 0xff) << 16;
+	MFPDELAY();
+
+	return( addr );
+}
+
+
+static int slm_write( struct inode *node, struct file *file,
+					  const char *buf, int count )
+
+{	int		device = MINOR( node->i_rdev );
+	int		n, filled, w, h;
+
+	while( SLMState == PRINTING ||
+		   (SLMState == FILLING && SLMBufOwner != device) ) {
+		interruptible_sleep_on( &slm_wait );
+		if (current->signal & ~current->blocked)
+			return( -ERESTARTSYS );
+	}
+	if (SLMState == IDLE) {
+		/* first data of page: get current page size  */
+		if (slm_get_pagesize( device, &w, &h ))
+			return( -EIO );
+		BufferSize = w*h/8;
+		if (BufferSize > SLM_BUFFER_SIZE)
+			return( -ENOMEM );
+
+		SLMState = FILLING;
+		SLMBufOwner = device;
+	}
+
+	n = count;
+	filled = BufferP - SLMBuffer;
+	if (filled + n > BufferSize)
+		n = BufferSize - filled;
+
+	memcpy_fromfs( BufferP, buf, n );
+	BufferP += n;
+	filled += n;
+
+	if (filled == BufferSize) {
+		/* Check the paper size again! The user may have switched it in the
+		 * time between starting the data and finishing them. Would end up in
+		 * a trashy page... */
+		if (slm_get_pagesize( device, &w, &h ))
+			return( -EIO );
+		if (BufferSize != w*h/8) {
+			printk( KERN_NOTICE "slm%d: page size changed while printing\n",
+					device );
+			return( -EAGAIN );
+		}
+
+		SLMState = PRINTING;
+		/* choose a slice size that is a multiple of the line size */
+#ifndef SLM_CONT_CNT_REPROG
+		SLMSliceSize = SLM_SLICE_SIZE(w);
+#endif
+		
+		start_print( device );
+		sleep_on( &print_wait );
+		if (SLMError && IS_REAL_ERROR(SLMError)) {
+			printk( KERN_ERR "slm%d: %s\n", device, slm_errstr(SLMError) );
+			n = -EIO;
+		}
+
+		SLMState = IDLE;
+		BufferP = SLMBuffer;
+		wake_up_interruptible( &slm_wait );
+	}
+	
+	return( n );
+}
+
+
+/* ---------------------------------------------------------------------- */
+/*							   ioctl Functions							  */
+
+
+static int slm_ioctl( struct inode *inode, struct file *file,
+					  unsigned int cmd, unsigned long arg )
+
+{	int		device = MINOR( inode->i_rdev ), err;
+	
+	/* I can think of setting:
+	 *  - manual feed
+	 *  - paper format
+	 *  - copy count
+	 *  - ...
+	 * but haven't implemented that yet :-)
+	 * BTW, has anybody better docs about the MODE SENSE/MODE SELECT data?
+	 */
+	switch( cmd ) {
+
+	  case SLMIORESET:		/* reset buffer, i.e. empty the buffer */
+		if (!(file->f_mode & 2))
+			return( -EINVAL );
+		if (SLMState == PRINTING)
+			return( -EBUSY );
+		SLMState = IDLE;
+		BufferP = SLMBuffer;
+		wake_up_interruptible( &slm_wait );
+		return( 0 );
+		
+	  case SLMIOGSTAT: {	/* get status */
+		int stat;
+		char *str;
+
+		stat = slm_req_sense( device );
+		if (arg) {
+			err = verify_area( VERIFY_WRITE, (long *)arg,
+							   sizeof(struct SLM_status) );
+			if (err) return( err );
+
+			str = slm_errstr( stat );
+			put_fs_long( stat, &((struct SLM_status *)arg)->stat );
+			memcpy_tofs( ((struct SLM_status *)arg)->str, str,
+						 strlen(str) + 1 );
+		}
+		return( stat );
+	  }
+		
+	  case SLMIOGPSIZE: {	/* get paper size */
+		int w, h;
+		
+		err = verify_area( VERIFY_WRITE, (long *)arg,
+						   sizeof(struct SLM_paper_size) );
+		if (err) return( err );
+
+		if ((err = slm_get_pagesize( device, &w, &h ))) return( err );
+		
+		put_fs_long( w, &((struct SLM_paper_size *)arg)->width );
+		put_fs_long( h, &((struct SLM_paper_size *)arg)->height );
+		return( 0 );
+	  }
+		
+	  case SLMIOGMFEED:	/* get manual feed */
+		return( -EINVAL );
+
+	  case SLMIOSPSIZE:	/* set paper size */
+		return( -EINVAL );
+
+	  case SLMIOSMFEED:	/* set manual feed */
+		return( -EINVAL );
+
+	}
+	return( -EINVAL );
+}
+
+
+/* ---------------------------------------------------------------------- */
+/*							 Opening and Closing						  */
+
+
+static int slm_open( struct inode *inode, struct file *file )
+
+{	int device;
+	struct slm *sip;
+	
+	device = MINOR(inode->i_rdev);
+	if (device >= N_SLM_Printers)
+		return( -ENXIO );
+	sip = &slm_info[device];
+
+	if (file->f_mode & 2) {
+		/* open for writing is exclusive */
+		if (sip->wbusy)
+			return( -EBUSY );
+		sip->wbusy = 1;
+	}
+	if (file->f_mode & 1) {
+		/* open for writing is exclusive */
+		if (sip->rbusy)
+			return( -EBUSY );
+		sip->rbusy = 1;
+	}
+
+	return( 0 );
+}
+
+
+static void slm_release( struct inode *inode, struct file *file )
+
+{	int device;
+	struct slm *sip;
+	
+	device = MINOR(inode->i_rdev);
+	sip = &slm_info[device];
+
+	if (file->f_mode & 2)
+		sip->wbusy = 0;
+	if (file->f_mode & 1)
+		sip->rbusy = 0;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/*						 ACSI Primitives for the SLM					  */
+
+
+static int slm_req_sense( int device )
+
+{	int			stat, rv;
+	struct slm *sip = &slm_info[device];
+	
+	stdma_lock( NULL, NULL );
+
+	CMDSET_TARG_LUN( slmreqsense_cmd, sip->target, sip->lun );
+	if (!acsicmd_nodma( slmreqsense_cmd, 0 ) ||
+		(stat = acsi_getstatus()) < 0)
+		rv = SLMSTAT_ACSITO;
+	else
+		rv = stat & 0x1f;
+
+	ENABLE_IRQ();
+	stdma_release();
+	return( rv );
+}
+
+
+static int slm_mode_sense( int device, char *buffer, int abs_flag )
+
+{	unsigned char	stat, len;
+	int				rv = 0;
+	struct slm		*sip = &slm_info[device];
+	
+	stdma_lock( NULL, NULL );
+
+	CMDSET_TARG_LUN( slmmsense_cmd, sip->target, sip->lun );
+	slmmsense_cmd[5] = abs_flag ? 0x80 : 0;
+	if (!acsicmd_nodma( slmmsense_cmd, 0 )) {
+		rv = SLMSTAT_ACSITO;
+		goto the_end;
+	}
+
+	if (!acsi_extstatus( &stat, 1 )) {
+		acsi_end_extstatus();
+		rv = SLMSTAT_ACSITO;
+		goto the_end;
+	}
+	
+	if (!acsi_extstatus( &len, 1 )) {
+		acsi_end_extstatus();
+		rv = SLMSTAT_ACSITO;
+		goto the_end;
+	}
+	buffer[0] = len;
+	if (!acsi_extstatus( buffer+1, len )) {
+		acsi_end_extstatus();
+		rv = SLMSTAT_ACSITO;
+		goto the_end;
+	}
+	
+	acsi_end_extstatus();
+	rv = stat & 0x1f;
+
+  the_end:
+	ENABLE_IRQ();
+	stdma_release();
+	return( rv );
+}
+
+
+static int slm_mode_select( int device, char *buffer, int len,
+							int default_flag )
+
+{	int			stat, rv;
+	struct slm	*sip = &slm_info[device];
+	
+	stdma_lock( NULL, NULL );
+
+	CMDSET_TARG_LUN( slmmselect_cmd, sip->target, sip->lun );
+	slmmselect_cmd[5] = default_flag ? 0x80 : 0;
+	if (!acsicmd_nodma( slmmselect_cmd, 0 )) {
+		rv = SLMSTAT_ACSITO;
+		goto the_end;
+	}
+
+	if (!default_flag) {
+		unsigned char c = len;
+		if (!acsi_extcmd( &c, 1 )) {
+			rv = SLMSTAT_ACSITO;
+			goto the_end;
+		}
+		if (!acsi_extcmd( buffer, len )) {
+			rv = SLMSTAT_ACSITO;
+			goto the_end;
+		}
+	}
+	
+	stat = acsi_getstatus();
+	rv = (stat < 0 ? SLMSTAT_ACSITO : stat);
+
+  the_end:
+	ENABLE_IRQ();
+	stdma_release();
+	return( rv );
+}
+
+
+static int slm_get_pagesize( int device, int *w, int *h )
+
+{	char	buf[256];
+	int		stat;
+	
+	stat = slm_mode_sense( device, buf, 0 );
+	ENABLE_IRQ();
+	stdma_release();
+
+	if (stat != SLMSTAT_OK)
+		return( -EIO );
+
+	*w = (buf[3] << 8) | buf[4];
+	*h = (buf[1] << 8) | buf[2];
+	return( 0 );
+}
+
+
+/* ---------------------------------------------------------------------- */
+/*								Initialization							  */
+
+
+void attach_slm( int target, int lun )
+
+{	static int	did_register = 0;
+	int			len;
+
+	if (N_SLM_Printers >= MAX_SLM) {
+		printk( KERN_WARNING "Too much SLMs\n" );
+		return;
+	}
+	
+	/* do an INQUIRY */
+	udelay(100);
+	CMDSET_TARG_LUN( slminquiry_cmd, target, lun );
+	if (!acsicmd_nodma( slminquiry_cmd, 0 )) {
+	  inq_timeout:
+		printk( KERN_ERR "SLM inquiry command timed out.\n" );
+	  inq_fail:
+		acsi_end_extstatus();
+		return;
+	}
+	/* read status and header of return data */
+	if (!acsi_extstatus( SLMBuffer, 6 ))
+		goto inq_timeout;
+
+	if (SLMBuffer[1] != 2) { /* device type == printer? */
+		printk( KERN_ERR "SLM inquiry returned device type != printer\n" );
+		goto inq_fail;
+	}
+	len = SLMBuffer[5];
+	
+	/* read id string */
+	if (!acsi_extstatus( SLMBuffer, len ))
+		goto inq_timeout;
+	acsi_end_extstatus();
+	SLMBuffer[len] = 0;
+
+	if (!did_register) {
+		did_register = 1;
+	}
+
+	slm_info[N_SLM_Printers].target = target;
+	slm_info[N_SLM_Printers].lun    = lun;
+	slm_info[N_SLM_Printers].wbusy  = 0;
+	slm_info[N_SLM_Printers].rbusy  = 0;
+	
+	printk( "  Printer: %s\n", SLMBuffer );
+	printk( "Detected slm%d at id %d lun %d\n", N_SLM_Printers, target, lun );
+	N_SLM_Printers++;
+}
+
+
+int slm_init( void )
+
+{
+	if (register_chrdev( MAJOR_NR, "slm", &slm_fops )) {
+		printk( KERN_ERR "Unable to get major %d for ACSI SLM\n", MAJOR_NR );
+		return -EBUSY;
+	}
+	
+	if (!(SLMBuffer = kmalloc( SLM_BUFFER_SIZE,  GFP_KERNEL | GFP_DMA))) {
+		printk( KERN_ERR "Unable to get SLM ST-Ram buffer.\n" );
+		return -ENOMEM;
+	}
+	BufferP = SLMBuffer;
+	SLMState = IDLE;
+	
+	return 0;
+}
diff -urN pc-2.0.21/drivers/block/amiflop.c linux-2.0.21/drivers/block/amiflop.c
--- pc-2.0.21/drivers/block/amiflop.c	Thu May 16 08:05:10 1996
+++ linux-2.0.21/drivers/block/amiflop.c	Tue Sep 17 19:51:35 1996
@@ -24,7 +24,7 @@
  *  - works but I think it's inefficient. (look in redo_fd_request)
  *    But the changes were very efficient. (only three and a half lines)
  *
- *  january 1995 added special ioctl for tracking down read/write problems
+ *  january 1996 added special ioctl for tracking down read/write problems
  *  - usage ioctl(d, RAW_TRACK, ptr); the raw track buffer (MFM-encoded data
  *    is copied to area. (area should be large enough since no checking is
  *    done - 30K is currently sufficient). return the actual size of the
@@ -32,6 +32,10 @@
  *  - replaced udelays() by a timer (CIAA timer B) for the waits 
  *    needed for the disk mechanic.
  *
+ *  february 1996 fixed error recovery and multiple disk access
+ *  - both got broken the first time I tampered with the driver :-(
+ *  - still not safe, but better than before
+ *
  *  revised Marts 3rd, 1996 by Jes Sorensen for use in the 1.3.28 kernel.
  *  - Minor changes to accept the kdev_t.
  *  - Replaced some more udelays with ms_delays. Udelay is just a loop,
@@ -43,6 +47,9 @@
  *    that when we start using 16 (24?) bit minors.
  */
 
+#ifdef MODULE
+#include <linux/module.h>
+#endif
 #include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/fcntl.h>
@@ -55,12 +62,12 @@
 #include <linux/string.h>
 #include <linux/mm.h>
 
+#include <asm/setup.h>
 #include <asm/amifdreg.h>
 #include <asm/amifd.h>
 #include <asm/amigahw.h>
 #include <asm/amigaints.h>
 #include <asm/irq.h>
-#include <asm/bootinfo.h>
 #include <asm/amigatypes.h>
 
 #define MAJOR_NR FLOPPY_MAJOR
@@ -126,15 +133,15 @@
 static struct fd_drive_type drive_types[] = {
 /*  code	name	   tr he   rdsz   wrsz sm pc1 pc2 sd  st st*/
 /*  warning: times are now in milliseconds (ms)                    */
- { FD_DD_3,	"DD 3.5", 160, 2, 14716, 13630, 1, 80,161, 3, 18, 1},
- { FD_HD_3,	"HD 3.5", 160, 2, 28344, 27258, 2, 80,161, 3, 18, 1},
- { FD_DD_5,	"DD 5.25", 80, 2, 14716, 13630, 1, 40, 81, 6, 30, 2},
+ { FD_DD_3,	"DD 3.5",  80, 2, 14716, 13630, 1, 80,161, 3, 18, 1},
+ { FD_HD_3,	"HD 3.5",  80, 2, 28344, 27258, 2, 80,161, 3, 18, 1},
+ { FD_DD_5,	"DD 5.25", 40, 2, 14716, 13630, 1, 40, 81, 6, 30, 2},
  { FD_NODRIVE, "No Drive", 0, 0,     0,     0, 0,  0,  0,  0,  0, 0}
 };
 static int num_dr_types = sizeof(drive_types) / sizeof(drive_types[0]);
 
 /* defaults for 3 1/2" HD-Disks */
-static int floppy_sizes[256]={880,880,880,880,720,720,720,};
+static int floppy_sizes[256]={880,880,880,880,720,720,720,720,};
 static int floppy_blocksizes[256]={0,};
 /* hardsector size assumed to be 512 */
 
@@ -142,7 +149,6 @@
   { "Amiga", 11 , amiga_read, amiga_write},
   { "MS-Dos", 9, dos_read, dos_write}
 };
-static int num_da_types = sizeof(data_types) / sizeof(data_types[0]);
 
 /* current info on each unit */
 static struct amiga_floppy_struct unit[FD_MAX_UNITS];
@@ -168,13 +174,20 @@
  * information to interrupts. They are the data used for the current
  * request.
  */
-static char block_flag = 0;
-static int selected = 0;
+static volatile char block_flag = 0;
+static volatile int selected = 0;
 static struct wait_queue *wait_fd_block = NULL;
 
-/* Synchronization of FDC access. */
-static volatile int fdc_busy = 0;
+/* Synchronization of FDC access */
+/* request loop (trackbuffer) */
+static volatile int fdc_busy = -1;
+static volatile int fdc_nested = 0;
 static struct wait_queue *fdc_wait = NULL;
+/* hardware */
+static volatile int hw_busy = -1;
+static volatile int hw_nested = 0;
+static struct wait_queue *hw_wait = NULL;
+ 
 static struct wait_queue *motor_wait = NULL;
 
 /* MS-Dos MFM Coding tables (should go quick and easy) */
@@ -185,28 +198,117 @@
 static unsigned char mfmdecode[128];
 
 /* floppy internal millisecond timer stuff */
-static struct semaphore ms_sem = MUTEX;
+static volatile int ms_busy = -1;
 static struct wait_queue *ms_wait = NULL;
 #define MS_TICKS ((amiga_eclock+50)/1000)
 
-static void ms_isr(int irq, struct pt_regs *fp, void *dummy)
+/*
+ * Note that MAX_ERRORS=X doesn't imply that we retry every bad read
+ * max X times - some types of errors increase the errorcount by 2 or
+ * even 3, so we might actually retry only X/2 times before giving up.
+ */
+#define MAX_ERRORS 12
+
+/*
+ * The driver is trying to determine the correct media format
+ * while probing is set. rw_interrupt() clears it after a
+ * successful access.
+ */
+static int probing = 0;
+
+/* Prevent "aliased" accesses. */
+static fd_ref[4] = { 0,0,0,0 };
+static fd_device[4] = { 0,0,0,0 };
+
+/*
+ * Current device number. Taken either from the block header or from the
+ * format request descriptor.
+ */
+#define CURRENT_DEVICE (CURRENT->rq_dev)
+
+/* Current error count. */
+#define CURRENT_ERRORS (CURRENT->errors)
+
+static void get_fdc(int drive)
 {
+unsigned long flags;
+
+       drive &= 3;
+       save_flags(flags);
+       cli();
+       if (fdc_busy != drive)
+         while (!(fdc_busy < 0))
+           sleep_on(&fdc_wait);
+       fdc_busy = drive;
+       fdc_nested++;
+       restore_flags(flags);
+}
+
+static inline void rel_fdc(void)
+{
+#ifdef DEBUG
+       if (fdc_nested == 0)
+         printk("fd: unmatched rel_fdc\n");
+#endif
+       fdc_nested--;
+       if (fdc_nested == 0) {
+         fdc_busy = -1;
+         wake_up(&fdc_wait);
+       }
+}
+
+static void get_hw(int drive)
+{
+unsigned long flags;
+
+       drive &= 3;
+       save_flags(flags);
+       cli();
+       if (hw_busy != drive)
+         while (!(hw_busy < 0))
+           sleep_on(&hw_wait);
+       hw_busy = drive;
+       hw_nested++;
+       restore_flags(flags);
+}
+
+static inline void rel_hw(void)
+{
+#ifdef DEBUG
+       if (hw_nested == 0)
+         printk("fd: unmatched hw_rel\n");
+#endif
+       hw_nested--;
+       if (hw_nested == 0) {
+         hw_busy = -1;
+         wake_up(&hw_wait);
+       }
+}
+
+static void ms_isr(int irq, void *dummy, struct pt_regs *fp)
+{
+ms_busy = -1;
 wake_up(&ms_wait);
 }
 
-/* with the semaphore waits are queued up 
+/* all waits are queued up 
    A more generic routine would do a schedule a la timer.device */
 static void ms_delay(int ms)
 {
+  unsigned long flags;
   int ticks;
   if (ms > 0) {
-    down(&ms_sem);
-    ticks=MS_TICKS*ms-1;
+    save_flags(flags);
+    cli();
+    while (ms_busy == 0)
+      sleep_on(&ms_wait);
+    ms_busy = 0;
+    restore_flags(flags);
+    ticks = MS_TICKS*ms-1;
     ciaa.tblo=ticks%256;
     ciaa.tbhi=ticks/256;
-    ciaa.crb=0x19; /* count clock, force load, one-shot, start */
+    ciaa.crb=0x19; /*count eclock, force load, one-shoot, start */
     sleep_on(&ms_wait);
-    up(&ms_sem);
   }
 }
 
@@ -223,6 +325,7 @@
 	unsigned char prb = ~0;
 
 	drive&=3;
+	get_hw(drive);
 	save_flags(flags);
 	cli();
 
@@ -238,6 +341,15 @@
 	selected = -1;
 	unit[drive].motor = 0;
 
+	rel_hw();
+#ifdef MODULE
+/*
+this is the last interrupt for any drive access, happens after
+release. So we have to wait until now to decrease the use count.
+*/
+       if (fd_ref[drive] == 0)
+         MOD_DEC_USE_COUNT;
+#endif
 	restore_flags(flags);
 }
 
@@ -260,6 +372,7 @@
 	unsigned char prb = ~0;
 
 	nr &= 3;
+	get_hw(nr);
 	save_flags (flags);
 	cli();
 	del_timer(motor_off_timer + nr);
@@ -284,12 +397,17 @@
 		while (!unit[nr].motor)
 			sleep_on (&motor_wait);
 	}
+	rel_hw();
 	restore_flags(flags);
 
 	if (on_attempts == 0) {
-		printk ("motor_on failed, turning motor off\n");
+#if 0
+		printk (KERN_ERR "motor_on failed, turning motor off\n");
 		fd_motor_off (nr);
 		return 0;
+#else
+		printk (KERN_WARNING "DSKRDY not set after 1.5 seconds - assuming drive is spinning notwithstanding\n");
+#endif
 	}
 
 	return 1;
@@ -310,6 +428,7 @@
 	drive&=3;
 	if (drive == selected)
 		return;
+	get_hw(drive);
 	selected = drive;
 
 	if (unit[drive].track % 2 != 0)
@@ -320,6 +439,7 @@
 	ciab.prb = prb;
 	prb &= ~SELMASK(drive);
 	ciab.prb = prb;
+	rel_hw();
 }
 
 static void fd_deselect (int drive)
@@ -331,6 +451,7 @@
 	if (drive != selected)
 		return;
 
+	get_hw(drive);
 	save_flags (flags);
 	sti();
 
@@ -341,6 +462,7 @@
 	ciab.prb = prb;
 
 	restore_flags (flags);
+	rel_hw();
 
 }
 
@@ -355,6 +477,7 @@
 	int n;
 
 	drive &= 3;
+	get_hw(drive);
 	if (!motor_on (drive))
 		return 0;
 	fd_select (drive);
@@ -362,38 +485,40 @@
 	prb |= DSKSIDE;
 	prb &= ~DSKDIREC;
 	ciab.prb = prb;
-	for (n = unit[drive].type->tracks/4; n != 0; --n) {
+	for (n = unit[drive].type->tracks/2; n != 0; --n) {
 		if (ciaa.pra & DSKTRACK0)
 			break;
 		prb &= ~DSKSTEP;
 		ciab.prb = prb;
 		prb |= DSKSTEP;
-		ms_delay (2);
+		udelay (2);
 		ciab.prb = prb;
 		ms_delay(unit[drive].type->step_delay);
 	}
 	ms_delay (unit[drive].type->settle_time);
 	prb |= DSKDIREC;
-	n = unit[drive].type->tracks/2 + 20;
+	n = unit[drive].type->tracks + 20;
 	for (;;) {
 		prb &= ~DSKSTEP;
 		ciab.prb = prb;
 		prb |= DSKSTEP;
-		ms_delay (2);
+		udelay (2);
 		ciab.prb = prb;
 		ms_delay(unit[drive].type->step_delay + 1);
 		if ((ciaa.pra & DSKTRACK0) == 0)
 			break;
 		if (--n == 0) {
-			printk ("calibrate failed, turning motor off\n");
+			printk (KERN_ERR "calibrate failed, turning motor off\n");
 			fd_motor_off (drive);
 			unit[drive].track = -1;
+			rel_hw();
 			return 0;
 		}
 	}
 	unit[drive].track = 0;
 	ms_delay(unit[drive].type->settle_time);
 
+	rel_hw();
 	return 1;
 }
 
@@ -408,13 +533,20 @@
 	int cnt;
 
 	drive &= 3;
-	if (unit[drive].track == track)
+	get_hw(drive);
+	if (unit[drive].track == track) {
+		rel_hw();
 		return 1;
-	if (!motor_on(drive))
+	}
+	if (!motor_on(drive)) {
+		rel_hw();
 		return 0;
+	}
 	fd_select (drive);
-	if (unit[drive].track < 0 && !fd_calibrate(drive))
+	if (unit[drive].track < 0 && !fd_calibrate(drive)) {
+		rel_hw();
 		return 0;
+	}
 
 	cnt = unit[drive].track/2 - track/2;
 	prb = ciab.prb;
@@ -429,18 +561,21 @@
 	if (track % 2 != unit[drive].track % 2)
 		ms_delay (unit[drive].type->side_time);
 	unit[drive].track = track;
-	if (cnt == 0)
+	if (cnt == 0) {
+		rel_hw();
 		return 1;
+	}
 	do {
 		prb &= ~DSKSTEP;
 		ciab.prb = prb;
 		prb |= DSKSTEP;
-		ms_delay (1);
+		udelay (1);
 		ciab.prb = prb;
 		ms_delay (unit[drive].type->step_delay);
 	} while (--cnt != 0);
 	ms_delay (unit[drive].type->settle_time);
 
+	rel_hw();
 	return 1;
 }
 
@@ -504,9 +639,6 @@
 	struct header hdr;
 	int i;
 
-	if (!AMIGAHW_PRESENT(AMI_FLOPPY))
-	    return 0;
-
 	disk&=3;
 	*raw = (raw[-1]&1) ? 0x2AAAAAAA : 0xAAAAAAAA;
 	raw++;
@@ -618,7 +750,7 @@
 
 	for (scnt = 0;scnt < unit[drive].sects; scnt++) {
 		if (!(raw = scan_sync(raw, end))) {
-			printk ("can't find sync for sector %d\n", scnt);
+			printk (KERN_INFO "can't find sync for sector %d\n", scnt);
 			return MFM_NOSYNC;
 		}
 
@@ -638,13 +770,13 @@
 #endif
 
 		if (hdr.hdrchk != csum) {
-			printk("MFM_HEADER: %08lx,%08lx\n", hdr.hdrchk, csum);
+			printk(KERN_INFO "MFM_HEADER: %08lx,%08lx\n", hdr.hdrchk, csum);
 			return MFM_HEADER;
 		}
 
 		/* verify track */
 		if (hdr.track != track) {
-			printk("MFM_TRACK: %d, %d\n", hdr.track, track);
+			printk(KERN_INFO "MFM_TRACK: %d, %d\n", hdr.track, track);
 			return MFM_TRACK;
 		}
 
@@ -653,10 +785,10 @@
 		csum = checksum((ulong *)(track_data + hdr.sect*512), 512);
 
 		if (hdr.datachk != csum) {
-			printk("MFM_DATA: (%x:%d:%d:%d) sc=%d %lx, %lx\n",
+			printk(KERN_INFO "MFM_DATA: (%x:%d:%d:%d) sc=%d %lx, %lx\n",
 			       hdr.magic, hdr.track, hdr.sect, hdr.ord, scnt,
 			       hdr.datachk, csum);
-			printk ("data=(%lx,%lx,%lx,%lx)\n",
+			printk (KERN_INFO "data=(%lx,%lx,%lx,%lx)\n",
 				((ulong *)(track_data+hdr.sect*512))[0],
 				((ulong *)(track_data+hdr.sect*512))[1],
 				((ulong *)(track_data+hdr.sect*512))[2],
@@ -852,7 +984,7 @@
   for (scnt=0;scnt<unit[drive].sects;scnt++) {
   do { /* search for the right sync of each sec-hdr */
     if (!(raw = scan_sync (raw, end))) {
-      printk("dos_read: no hdr sync on track %d, unit %d for sector %d\n",
+      printk(KERN_INFO "dos_read: no hdr sync on track %d, unit %d for sector %d\n",
         track,drive,scnt);
       return MFM_NOSYNC;
     }
@@ -870,30 +1002,30 @@
 #endif
 
   if (crc != hdr.crc) {
-    printk("dos_read: MFM_HEADER %04x,%04x\n", hdr.crc, crc);
+    printk(KERN_INFO "dos_read: MFM_HEADER %04x,%04x\n", hdr.crc, crc);
     return MFM_HEADER;
   }
   if (hdr.track != track/unit[drive].type->heads) {
-    printk("dos_read: MFM_TRACK %d, %d\n", hdr.track,
+    printk(KERN_INFO "dos_read: MFM_TRACK %d, %d\n", hdr.track,
       track/unit[drive].type->heads);
     return MFM_TRACK;
   }
 
   if (hdr.side != track%unit[drive].type->heads) {
-    printk("dos_read: MFM_SIDE %d, %d\n", hdr.side,
+    printk(KERN_INFO "dos_read: MFM_SIDE %d, %d\n", hdr.side,
       track%unit[drive].type->heads);
     return MFM_TRACK;
   }
 
   if (hdr.len_desc != 2) {
-    printk("dos_read: unknown sector len descriptor %d\n", hdr.len_desc);
+    printk(KERN_INFO "dos_read: unknown sector len descriptor %d\n", hdr.len_desc);
     return MFM_DATA;
   }
 #ifdef DEBUG
   printk("hdr accepted\n");
 #endif
   if (!(raw = scan_sync (raw, end))) {
-    printk("dos_read: no data sync on track %d, unit %d for sector%d, disk sector %d\n",
+    printk(KERN_INFO "dos_read: no data sync on track %d, unit %d for sector%d, disk sector %d\n",
       track, drive, scnt, hdr.sec);
     return MFM_NOSYNC;
   }
@@ -902,7 +1034,7 @@
 #endif
 
   if (*((ushort *)raw)!=0x5545) {
-    printk("dos_read: no data mark after sync (%d,%d,%d,%d) sc=%d\n",
+    printk(KERN_INFO "dos_read: no data mark after sync (%d,%d,%d,%d) sc=%d\n",
       hdr.track,hdr.side,hdr.sec,hdr.len_desc,scnt);
     return MFM_NOSYNC;
   }
@@ -913,10 +1045,10 @@
   crc = dos_data_crc(track_data + (hdr.sec - 1) * 512);
 
   if (crc != data_crc[0]) {
-    printk("dos_read: MFM_DATA (%d,%d,%d,%d) sc=%d, %x %x\n",
+    printk(KERN_INFO "dos_read: MFM_DATA (%d,%d,%d,%d) sc=%d, %x %x\n",
       hdr.track, hdr.side, hdr.sec, hdr.len_desc,
       scnt,data_crc[0], crc);
-    printk("data=(%lx,%lx,%lx,%lx,...)\n",
+    printk(KERN_INFO "data=(%lx,%lx,%lx,%lx,...)\n",
       ((ulong *)(track_data+(hdr.sec-1)*512))[0],
       ((ulong *)(track_data+(hdr.sec-1)*512))[1],
       ((ulong *)(track_data+(hdr.sec-1)*512))[2],
@@ -1039,33 +1171,6 @@
 *(ushort *)ptr = 0xaaa8; /* MFM word before is always 0x9254 */
 }
 
-/*
- * Note that MAX_ERRORS=X doesn't imply that we retry every bad read
- * max X times - some types of errors increase the errorcount by 2 or
- * even 3, so we might actually retry only X/2 times before giving up.
- */
-#define MAX_ERRORS 12
-
-/*
- * The driver is trying to determine the correct media format
- * while probing is set. rw_interrupt() clears it after a
- * successful access.
- */
-static int probing = 0;
-
-/* Prevent "aliased" accesses. */
-static fd_ref[4] = { 0,0,0,0 };
-static fd_device[4] = { 0,0,0,0 };
-
-/*
- * Current device number. Taken either from the block header or from the
- * format request descriptor.
- */
-#define CURRENT_DEVICE (CURRENT->rq_dev)
-
-/* Current error count. */
-#define CURRENT_ERRORS (CURRENT->errors)
-
 static void request_done(int uptodate)
 {
   timer_active &= ~(1 << FLOPPY_TIMER);
@@ -1084,13 +1189,15 @@
 	int changed;
 
 	if (MAJOR(dev) != MAJOR_NR) {
-		printk("floppy_change: not a floppy\n");
+		printk(KERN_CRIT "floppy_change: not a floppy\n");
 		return 0;
 	}
 
+	get_hw(drive);
 	fd_select (drive);
 	changed = !(ciaa.pra & DSKCHANGE);
 	fd_deselect (drive);
+	rel_hw();
 
 	if (changed) {
 		fd_probe(dev);
@@ -1119,6 +1226,7 @@
 static void raw_read(int drive, int track, char *ptrack, int len)
 {
 	drive&=3;
+	get_hw(drive);
 	/* setup adkcon bits correctly */
 	custom.adkcon = ADK_MSBSYNC;
 	custom.adkcon = ADK_SETCLR|ADK_WORDSYNC|ADK_FAST;
@@ -1139,6 +1247,7 @@
 		sleep_on (&wait_fd_block);
 
 	custom.dsklen = 0;
+	rel_hw();
 }
 
 static int raw_write(int drive, int track, char *ptrack, int len)
@@ -1149,6 +1258,7 @@
 	if ((ciaa.pra & DSKPROT) == 0)
 		return 0;
 
+	get_hw(drive); /* corresponds to rel_hw() in post_write() */
 	/* clear adkcon bits */
 	custom.adkcon = ADK_PRECOMP1|ADK_PRECOMP0|ADK_WORDSYNC|ADK_MSBSYNC;
 	/* set appropriate adkcon bits */
@@ -1176,30 +1286,45 @@
   custom.dsklen = 0;
   writepending = 0;
   writefromint = 0;
+  rel_hw(); /* corresponds to get_hw() in raw_write */
 }
 
 static int get_track(int drive, int track)
 {
-	int error;
+	int error, errors;
 
 	drive&=3;
-	if ((lastdrive == drive) && (savedtrack == track))
-		return 0;
-
-	lastdrive = drive;
-	raw_read(drive, track, raw_buf, unit[drive].type->read_size);
-	savedtrack = -1;
-	error = (*unit[drive].dtype->read_fkt)(drive, trackdata, (unsigned long)raw_buf, track);
-	switch (error) {
-	    case 0:
-		savedtrack = track;
-		return 0;
-	    case MFM_TRACK:
-		unit[drive].track = -1;
-		/* fall through */
-	    default:
-		return -1;
+        get_hw(drive);
+        if (!motor_on(drive)) {
+          rel_hw();
+          return -1;
+        }
+        fd_select(drive);
+        errors = 0;
+        while (errors < MAX_ERRORS) {
+          if (!fd_seek(drive, track)) {
+            rel_hw();
+            return -1; /* we can not calibrate - no chance */
+          }
+          if ((lastdrive == drive) && (savedtrack == track)) {
+            rel_hw();
+            return 0;
+          }
+          lastdrive = drive;
+          raw_read(drive, track, raw_buf, unit[drive].type->read_size);
+          savedtrack = -1;
+          error = (*unit[drive].dtype->read_fkt)(drive, trackdata, (unsigned long)raw_buf, track);
+          if (error == 0) {
+            savedtrack = track;
+            rel_hw();
+            return 0;
+          }
+          if (error == MFM_TRACK)
+            unit[drive].track = -1;
+          errors++;
 	}
+	rel_hw();
+	return -1;
 }
 
 static void flush_track_callback(unsigned long nr)
@@ -1208,7 +1333,7 @@
   writefromint = 1;
   (*unit[nr].dtype->write_fkt)(nr, (unsigned long)raw_buf, trackdata, savedtrack);
   if (!raw_write(nr, savedtrack, raw_buf, unit[nr].type->write_size)) {
-    printk ("floppy disk write protected\n");
+    printk (KERN_NOTICE "floppy disk write protected\n");
     writefromint = 0;
     writepending = 0;
   }
@@ -1227,7 +1352,7 @@
     restore_flags(flags);
     (*unit[nr].dtype->write_fkt)(nr, (unsigned long)raw_buf, trackdata, savedtrack);
     if (!raw_write(nr, savedtrack, raw_buf, unit[nr].type->write_size)) {
-      printk ("floppy disk write protected in write!\n");
+      printk (KERN_NOTICE "floppy disk write protected in write!\n");
       writepending = 0;
       return 0;
     }
@@ -1255,10 +1380,9 @@
 
     repeat:
 	if (!CURRENT) {
-		if (!fdc_busy)
-			printk("FDC access conflict!");
-		fdc_busy = 0;
-		wake_up(&fdc_wait);
+		if (fdc_busy < 0)
+			printk(KERN_CRIT "FDC access conflict!");
+		rel_fdc();
 		CLEAR_INTR;
 		return;
 	}
@@ -1324,15 +1448,6 @@
 
 		switch (CURRENT->cmd) {
 		    case READ:
-			if (!motor_on (drive)) {
-				end_request(0);
-				goto repeat;
-			}
-			fd_select (drive);
-			if (!fd_seek(drive, track)) {
-				end_request(0);
-				goto repeat;
-			}
 			if (get_track(drive, track) == -1) {
 				end_request(0);
 				goto repeat;
@@ -1341,15 +1456,6 @@
 			break;
 
 		    case WRITE:
-			if (!motor_on (drive)) {
-				end_request(0);
-				goto repeat;
-			}
-			fd_select (drive);
-			if (!fd_seek(drive, track)) {
-				end_request(0);
-				goto repeat;
-			}
 			if (get_track(drive, track) == -1) {
 				end_request(0);
 				goto repeat;
@@ -1374,7 +1480,7 @@
 			break;
 
 		    default:
-			printk("do_fd_request: unknown command\n");
+			printk(KERN_WARNING "do_fd_request: unknown command\n");
 			request_done(0);
 			goto repeat;
 		}
@@ -1388,13 +1494,7 @@
 
 static void do_fd_request(void)
 {
-unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	while (fdc_busy) sleep_on(&fdc_wait);
-	fdc_busy = 1;
-	restore_flags(flags); /* sti(); */
+	get_fdc(CURRENT_DEVICE & 3);
 	redo_fd_request();
 }
 
@@ -1404,22 +1504,33 @@
 	int drive = inode->i_rdev & 3;
 	static struct floppy_struct getprm;
 	int error;
+	unsigned long flags;
 
 	switch(cmd)
 	 {
 	  case FDFMTBEG:
-	    if (fd_ref[drive] > 1)
+	  get_hw(drive);
+	    if (fd_ref[drive] > 1) {
+	      rel_hw();
 	      return -EBUSY;
+	    }
 	    fsync_dev(inode->i_rdev);
-	    if (motor_on(drive) == 0)
+	    if (motor_on(drive) == 0) {
+	      rel_hw();
 	      return -ENODEV;
-	    if (fd_calibrate(drive) == 0)
+	    }
+	    if (fd_calibrate(drive) == 0) {
+	      rel_hw();
 	      return -ENXIO;
+	    }
 	    floppy_off(drive);
+	    rel_hw();
 	    break;
 	  case FDFMTTRK:
-	    if (param < unit[drive].type->tracks)
+	    if (param < unit[drive].type->tracks * unit[drive].type->heads)
 	     {
+	      get_fdc(drive);
+	      get_hw(drive);
 	      fd_select(drive);
 	      if (fd_seek(drive,param)!=0)
 	       {
@@ -1428,6 +1539,8 @@
 	        non_int_flush_track(drive);
 	       }
 	      floppy_off(drive);
+	      rel_hw();
+	      rel_fdc();
 	     }
 	    else
 	      return -EINVAL;
@@ -1443,7 +1556,7 @@
 	    if (error)
 	      return error;
 	    memset((void *)&getprm, 0, sizeof (getprm));
-	    getprm.track=unit[drive].type->tracks/unit[drive].type->heads;
+	    getprm.track=unit[drive].type->tracks;
 	    getprm.head=unit[drive].type->heads;
 	    getprm.sect=unit[drive].sects;
 	    getprm.size=unit[drive].blocks;
@@ -1460,10 +1573,15 @@
           case FDDEFPRM:
             return -EINVAL;
           case FDFLUSH:
+	    save_flags(flags);
+	    cli();
             if ((drive == selected) && (writepending)) {
               del_timer (&flush_track_timer);
+              restore_flags(flags);
               non_int_flush_track(selected);
             }
+            else
+              restore_flags(flags);
             break;
 #ifdef RAW_IOCTL
 	  case IOCTL_RAW_TRACK:
@@ -1475,7 +1593,7 @@
 	    return unit[drive].type->read_size;
 #endif
 	  default:
-	    printk("fd_ioctl: unknown cmd %d for drive %d.",cmd,drive);
+	    printk(KERN_DEBUG "fd_ioctl: unknown cmd %d for drive %d.",cmd,drive);
 	    return -ENOSYS;
          }
 	return 0;
@@ -1486,10 +1604,12 @@
 ======================================================================*/
 static unsigned long get_drive_id(int drive)
 {
+	static int called = 0;
 	int i;
 	ulong id = 0;
 
   	drive&=3;
+  	get_hw(drive);
 	/* set up for ID */
 	MOTOR_ON;
 	udelay(2);
@@ -1517,6 +1637,7 @@
 	}
 
 	selected = -1;
+	rel_hw();
 
         /*
          * RB: At least A500/A2000's df0: don't identify themselves.
@@ -1527,7 +1648,10 @@
         if(drive == 0 && id == FD_NODRIVE)
          {
                 id = fd_def_df0;
-                printk("fd: drive 0 didn't identify, setting default %08lx\n",(ulong)fd_def_df0);
+                printk("%sfd: drive 0 didn't identify, setting default %08lx\n",
+			(called == 0)? KERN_NOTICE:"", (ulong)fd_def_df0);
+		if (called == 0)
+			called++;
          }
 	/* return the ID value */
 	return (id);
@@ -1550,7 +1674,7 @@
 			break;
 
 	if (type >= num_dr_types) {
-		printk("fd_probe: unsupported drive type %08lx found\n",
+		printk(KERN_WARNING "fd_probe: unsupported drive type %08lx found\n",
 		       code);
 		return;
 	}
@@ -1623,9 +1747,11 @@
   if (filp && (filp->f_flags & (O_WRONLY|O_RDWR))) {
 	  int wrprot;
 
+	  get_hw(drive);
 	  fd_select (drive);
 	  wrprot = !(ciaa.pra & DSKPROT);
 	  fd_deselect (drive);
+	  rel_hw();
 
 	  if (wrprot)
 		  return -EROFS;
@@ -1637,9 +1763,12 @@
   unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks*
         unit[drive].sects;
 
-printk("fd%d: accessing %s-disk with %s-layout\n",drive,unit[drive].type->name,
+printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive,unit[drive].type->name,
   data_types[system].name);
 
+#ifdef MODULE
+  MOD_INC_USE_COUNT;
+#endif
   return 0;
 }
 
@@ -1660,9 +1789,13 @@
     restore_flags (flags);
   
   if (!fd_ref[inode->i_rdev & 3]--) {
-    printk("floppy_release with fd_ref == 0");
+    printk(KERN_CRIT "floppy_release with fd_ref == 0");
     fd_ref[inode->i_rdev & 3] = 0;
   }
+#ifdef MODULE
+/* the mod_use counter is handled this way */
+  floppy_off (inode->i_rdev & 3);
+#endif
 }
 
 void amiga_floppy_setup (char *str, int *ints)
@@ -1687,7 +1820,7 @@
 	NULL,			/* revalidate */
 };
 
-static void fd_block_done(int irq, struct pt_regs *fp, void *dummy)
+static void fd_block_done(int irq, void *dummy, struct pt_regs *fp)
 {
   if (block_flag)
     custom.dsklen = 0x4000;
@@ -1757,6 +1890,8 @@
 
   if (fd_def_df0==0) {
     if ((boot_info.bi_amiga.model == AMI_3000) ||
+        (boot_info.bi_amiga.model == AMI_3000T) ||
+        (boot_info.bi_amiga.model == AMI_3000PLUS) ||
         (boot_info.bi_amiga.model == AMI_4000))
       fd_def_df0=FD_HD_3;
     else
@@ -1775,8 +1910,33 @@
   /* make sure that disk DMA is enabled */
   custom.dmacon = DMAF_SETCLR | DMAF_DISK;
 
-  add_isr(IRQ_FLOPPY, fd_block_done, 0, NULL, "floppy_dma");
-  add_isr(IRQ_AMIGA_CIAA_TB, ms_isr, 0, NULL, "floppy_timer");
+  /* init ms timer */
+  ciaa.crb = 8; /* one-shot, stop */
+
+  request_irq(IRQ_FLOPPY, fd_block_done, 0, "floppy_dma", NULL);
+  request_irq(IRQ_AMIGA_CIAA_TB, ms_isr, 0, "floppy_timer", NULL);
 
   return 0;
 }
+
+#ifdef MODULE
+#include <linux/version.h>
+
+int init_module(void)
+{
+  return amiga_floppy_init();
+}
+
+void cleanup_module(void)
+{
+free_irq(IRQ_AMIGA_CIAA_TB, NULL);
+free_irq(IRQ_FLOPPY, NULL);
+custom.dmacon = DMAF_DISK; /* disable DMA */
+amiga_chip_free(raw_buf);
+timer_active &= ~(1 << FLOPPY_TIMER);
+blk_size[MAJOR_NR] = NULL;
+blksize_size[MAJOR_NR] = NULL;
+blk_dev[MAJOR_NR].request_fn = NULL;
+unregister_blkdev(MAJOR_NR, "fd");
+}
+#endif
diff -urN pc-2.0.21/drivers/block/ataflop.c linux-2.0.21/drivers/block/ataflop.c
--- pc-2.0.21/drivers/block/ataflop.c	Thu May 16 08:05:10 1996
+++ linux-2.0.21/drivers/block/ataflop.c	Sat Aug 24 14:33:11 1996
@@ -70,12 +70,12 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 
+#include <asm/setup.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/irq.h>
 #include <asm/pgtable.h>
 
-#include <asm/bootinfo.h>
 #include <asm/atafd.h>
 #include <asm/atafdreg.h>
 #include <asm/atarihw.h>
@@ -359,7 +359,7 @@
 static void check_change( void );
 static __inline__ void set_head_settle_flag( void );
 static __inline__ int get_head_settle_flag( void );
-static void floppy_irq (int irq, struct pt_regs *fp, void *dummy);
+static void floppy_irq (int irq, void *dummy, struct pt_regs *fp);
 static void fd_error( void );
 static int do_format(kdev_t drive, struct atari_format_descr *desc);
 static void do_fd_action( int drive );
@@ -572,7 +572,7 @@
 
 static void (*FloppyIRQHandler)( int status ) = NULL;
 
-static void floppy_irq (int irq, struct pt_regs *fp, void *dummy)
+static void floppy_irq (int irq, void *dummy, struct pt_regs *fp)
 {
 	unsigned char status;
 	void (*handler)( int );
@@ -1065,9 +1065,12 @@
 	    !(read_track && FDC_READ(FDCREG_SECTOR) > SUDT->spt)) {
 		if (Probing) {
 			if (SUDT > disk_type) {
+			    if (SUDT[-1].blocks > ReqBlock) {
 				/* try another disk type */
 				SUDT--;
 				floppy_sizes[SelectedDrive] = SUDT->blocks >> 1;
+			    } else
+				Probing = 0;
 			}
 			else {
 				if (SUD.flags & FTD_MSG)
@@ -1761,7 +1764,7 @@
   drive = MINOR (inode->i_rdev) & 3;
   type  = MINOR(inode->i_rdev) >> 2;
   DPRINT(("fd_open: type=%d\n",type));
-  if (type > NUM_DISK_MINORS)
+  if (drive >= FD_MAX_UNITS || type > NUM_DISK_MINORS)
 	return -ENXIO;
 
   old_dev = fd_device[drive];
diff -urN pc-2.0.21/drivers/block/genhd.c linux-2.0.21/drivers/block/genhd.c
--- pc-2.0.21/drivers/block/genhd.c	Tue Aug 20 15:44:45 1996
+++ linux-2.0.21/drivers/block/genhd.c	Sat Sep 14 15:17:49 1996
@@ -28,6 +28,7 @@
 #include <linux/blk.h>
 #endif
 
+#include <asm/setup.h>
 #include <asm/system.h>
 
 /*
@@ -36,10 +37,11 @@
  * table entries.
  */
 #include <asm/unaligned.h>
+#include <linux/msdos_fs.h>
 
 #define SYS_IND(p)	get_unaligned(&p->sys_ind)
-#define NR_SECTS(p)	get_unaligned(&p->nr_sects)
-#define START_SECT(p)	get_unaligned(&p->start_sect)
+#define NR_SECTS(p)	CF_LE_L(get_unaligned(&p->nr_sects))
+#define START_SECT(p)	CF_LE_L(get_unaligned(&p->start_sect))
 
 
 struct gendisk *gendisk_head = NULL;
@@ -140,7 +142,7 @@
 	   */
 		bh->b_state = 0;
 
-		if (*(unsigned short *) (bh->b_data+510) != 0xAA55)
+		if (*(unsigned short *) (bh->b_data+510) != CF_LE_W(0xAA55))
 			goto done;
 
 		p = (struct partition *) (0x1BE + bh->b_data);
@@ -265,7 +267,7 @@
 #ifdef CONFIG_BLK_DEV_IDE
 check_table:
 #endif
-	if (*(unsigned short *)  (0x1fe + data) != 0xAA55) {
+	if (*(unsigned short *)  (0x1fe + data) != CF_LE_W(0xAA55)) {
 		brelse(bh);
 		return 0;
 	}
@@ -277,7 +279,7 @@
 		 * Look for various forms of IDE disk geometry translation
 		 */
 		extern int ide_xlate_1024(kdev_t, int, const char *);
-		unsigned int sig = *(unsigned short *)(data + 2);
+		unsigned int sig = CF_LE_W(*(unsigned short *)(data + 2));
 		if (SYS_IND(p) == EZD_PARTITION) {
 			/*
 			 * The remainder of the disk must be accessed using
@@ -307,7 +309,7 @@
 				brelse(bh);
 				goto read_mbr;	/* start over with new MBR */
 			}
-		} else if (sig <= 0x1ae && *(unsigned short *)(data + sig) == 0x55AA
+		} else if (sig <= 0x1ae && *(unsigned short *)(data + sig) == CF_LE_W(0x55AA)
 			 && (1 & *(unsigned char *)(data + sig + 2)) ) 
 		{
 			/*
@@ -342,6 +344,7 @@
 	}
 #endif	/* CONFIG_BLK_DEV_IDE */
 
+	printk(" DOS");	/* show partition type */
 	current_minor += 4;  /* first "extra" minor (for extended partitions) */
 	for (i=1 ; i<=4 ; minor++,i++,p++) {
 		if (!NR_SECTS(p))
@@ -366,7 +369,7 @@
 		}
 #ifdef CONFIG_BSD_DISKLABEL
 		if (SYS_IND(p) == BSD_PARTITION) {
-			printk(" <");
+			printk(" BSD<");
 			bsd_disklabel_partition(hd, MKDEV(hd->major, minor));
 			printk(" >");
 		}
@@ -375,7 +378,7 @@
 	/*
 	 *  Check for old-style Disk Manager partition table
 	 */
-	if (*(unsigned short *) (data+0xfc) == 0x55AA) {
+	if (*(unsigned short *) (data+0xfc) == CF_LE_W(0x55AA)) {
 		p = (struct partition *) (0x1be + data);
 		for (i = 4 ; i < 16 ; i++, current_minor++) {
 			p--;
@@ -450,6 +453,7 @@
 		brelse(bh);
 		return 0;
 	}
+	printk(" OSF");
 	for (i = 0 ; i < label->d_npartitions; i++, partition++) {
 		if ((current_minor & mask) == 0)
 		        break;
@@ -517,8 +521,10 @@
 	label = (struct sun_disklabel *) bh->b_data;
 	p = label->partitions;
 	if (label->magic != SUN_LABEL_MAGIC && label->magic != SUN_LABEL_MAGIC_SWAPPED) {
+#if 0
 		printk("Dev %s Sun disklabel: bad magic %04x\n",
 		       kdevname(dev), label->magic);
+#endif
 		brelse(bh);
 		return 0;
 	}
@@ -533,6 +539,7 @@
 		brelse(bh);
 		return 0;
 	}
+	printk(" UFS");
 	/* All Sun disks have 8 partition entries */
 	spc = SWAP16(label->ntrks) * SWAP16(label->nsect);
 	for(i=0; i < 8; i++, p++) {
@@ -569,7 +576,7 @@
 }
 
 static int
-amiga_partition(struct gendisk *hd, unsigned int dev, unsigned long first_sector)
+amiga_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector)
 {
 	struct buffer_head	*bh;
 	struct RigidDiskBlock	*rdb;
@@ -584,13 +591,15 @@
 
 	for (blk = 0; blk < RDB_ALLOCATION_LIMIT; blk++) {
 		if(!(bh = bread(dev,blk,512))) {
-			printk("Dev %d: unable to read RDB block %d\n",dev,blk);
+			printk("Dev %s: unable to read RDB block %d\n",
+			       kdevname(dev),blk);
 			goto rdb_done;
 		}
 		if (*(__u32 *)bh->b_data == htonl(IDNAME_RIGIDDISK)) {
 			rdb = (struct RigidDiskBlock *)bh->b_data;
 			if (checksum_block((__u32 *)bh->b_data,htonl(rdb->rdb_SummedLongs) & 0x7F)) {
-				printk("Dev %d: RDB in block %d has bad checksum\n",dev,blk);
+				printk("Dev %s: RDB in block %d has bad checksum\n",
+				       kdevname(dev),blk);
 				brelse(bh);
 				continue;
 			}
@@ -599,8 +608,8 @@
 			brelse(bh);
 			for (part = 1; blk > 0 && part <= 16; part++) {
 				if (!(bh = bread(dev,blk,512))) {
-					printk("Dev %d: unable to read partition block %d\n",
-						       dev,blk);
+					printk("Dev %s: unable to read partition block %d\n",
+						       kdevname(dev),blk);
 					goto rdb_done;
 				}
 				pb  = (struct PartitionBlock *)bh->b_data;
@@ -636,6 +645,141 @@
 }
 #endif /* CONFIG_AMIGA_PARTITION */
 
+#ifdef CONFIG_ATARI_PARTITION
+#include <asm/atari_rootsec.h>
+
+/* ++guenther: this should be settable by the user ("make config")?.
+ */
+#define ICD_PARTS
+
+static int atari_partition (struct gendisk *hd, kdev_t dev,
+			    unsigned long first_sector)
+{
+  int minor = current_minor, m_lim = current_minor + hd->max_p;
+  struct buffer_head *bh;
+  struct rootsector *rs;
+  struct partition_info *pi;
+  ulong extensect;
+#ifdef ICD_PARTS
+  int part_fmt = 0; /* 0:unknown, 1:AHDI, 2:ICD/Supra */
+#endif
+
+  bh = bread (dev, 0, 1024);
+  if (!bh)
+    {
+      printk (" unable to read block 0\n");
+      return -1;
+    }
+
+  rs = (struct rootsector *) bh->b_data;
+  pi = &rs->part[0];
+  printk (" AHDI");
+  for (; pi < &rs->part[4] && minor < m_lim; minor++, pi++)
+    {
+      if (pi->flg & 1)
+	/* active partition */
+	{
+	  if (memcmp (pi->id, "XGM", 3) == 0)
+	    /* extension partition */
+	    {
+	      struct rootsector *xrs;
+	      struct buffer_head *xbh;
+	      ulong partsect;
+
+#ifdef ICD_PARTS
+	      part_fmt = 1;
+#endif
+	      printk(" XGM<");
+	      partsect = extensect = pi->st;
+	      while (1)
+		{
+		  xbh = bread (dev, partsect / 2, 1024);
+		  if (!xbh)
+		    {
+		      printk (" block %ld read failed\n", partsect);
+		      brelse(bh);
+		      return 0;
+		    }
+		  if (partsect & 1)
+		    xrs = (struct rootsector *) &xbh->b_data[512];
+		  else
+		    xrs = (struct rootsector *) &xbh->b_data[0];
+
+		  /* ++roman: sanity check: bit 0 of flg field must be set */
+		  if (!(xrs->part[0].flg & 1)) {
+		    printk( "\nFirst sub-partition in extended partition is not valid!\n" );
+		    break;
+		  }
+
+		  add_partition(hd, minor, partsect + xrs->part[0].st,
+				xrs->part[0].siz);
+
+		  if (!(xrs->part[1].flg & 1)) {
+		    /* end of linked partition list */
+		    brelse( xbh );
+		    break;
+		  }
+		  if (memcmp( xrs->part[1].id, "XGM", 3 ) != 0) {
+		    printk( "\nID of extended partition is not XGM!\n" );
+		    brelse( xbh );
+		    break;
+		  }
+
+		  partsect = xrs->part[1].st + extensect;
+		  brelse (xbh);
+		  minor++;
+		  if (minor >= m_lim) {
+		    printk( "\nMaximum number of partitions reached!\n" );
+		    break;
+		  }
+		}
+	      printk(" >");
+	    }
+	  else
+	    {
+	      /* we don't care about other id's */
+	      add_partition (hd, minor, pi->st, pi->siz);
+	    }
+	}
+    }
+#ifdef ICD_PARTS
+  if ( part_fmt!=1 ) /* no extended partitions -> test ICD-format */
+  {
+    pi = &rs->icdpart[0];
+    /* sanity check: no ICD format if first partition invalid */
+    if (memcmp (pi->id, "GEM", 3) == 0 ||
+        memcmp (pi->id, "BGM", 3) == 0 ||
+        memcmp (pi->id, "LNX", 3) == 0 ||
+        memcmp (pi->id, "SWP", 3) == 0 ||
+        memcmp (pi->id, "RAW", 3) == 0 )
+    {
+      printk(" ICD<");
+      for (; pi < &rs->icdpart[8] && minor < m_lim; minor++, pi++)
+      {
+        /* accept only GEM,BGM,RAW,LNX,SWP partitions */
+        if (pi->flg & 1 && 
+            (memcmp (pi->id, "GEM", 3) == 0 ||
+             memcmp (pi->id, "BGM", 3) == 0 ||
+             memcmp (pi->id, "LNX", 3) == 0 ||
+             memcmp (pi->id, "SWP", 3) == 0 ||
+             memcmp (pi->id, "RAW", 3) == 0) )
+        {
+          part_fmt = 2;
+	  add_partition (hd, minor, pi->st, pi->siz);
+        }
+      }
+      printk(" >");
+    }
+  }
+#endif
+  brelse (bh);
+
+  printk ("\n");
+
+  return 1;
+}
+#endif /* CONFIG_ATARI_PARTITION */
+
 static void check_partition(struct gendisk *hd, kdev_t dev)
 {
 	static int first_time = 1;
@@ -671,6 +815,10 @@
 #endif
 #ifdef CONFIG_AMIGA_PARTITION
 	if(amiga_partition(hd, dev, first_sector))
+		return;
+#endif
+#ifdef CONFIG_ATARI_PARTITION
+	if(atari_partition(hd, dev, first_sector))
 		return;
 #endif
 	printk(" unknown partition table\n");
diff -urN pc-2.0.21/drivers/block/ide.c linux-2.0.21/drivers/block/ide.c
--- pc-2.0.21/drivers/block/ide.c	Mon Aug 12 09:24:26 1996
+++ linux-2.0.21/drivers/block/ide.c	Mon Aug 26 23:56:34 1996
@@ -20,7 +20,7 @@
  *
  * It is easy to extend ide.c to handle more than four interfaces:
  *
- *	Change the MAX_HWIFS constant in ide.h.
+ *	Change the MAX_HWIFS constant in include/asm/ide.h.
  *
  *	Define some new major numbers (in major.h), and insert them into
  *	the ide_hwif_to_major table in ide.c.
@@ -284,6 +284,7 @@
 #include <linux/genhd.h>
 #include <linux/malloc.h>
 
+#include <asm/setup.h>
 #include <asm/byteorder.h>
 #include <asm/irq.h>
 #include <asm/segment.h>
@@ -304,9 +305,15 @@
 #define IS_PROMISE_DRIVE (0)	/* auto-NULLs out Promise code */
 #endif /* CONFIG_BLK_DEV_PROMISE */
 
+#if (MAX_HWIFS > 1)	/* We only consider the cases 1 and 4 */
 static const byte	ide_hwif_to_major[MAX_HWIFS] = {IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR};
 static const unsigned short default_io_base[MAX_HWIFS] = {0x1f0, 0x170, 0x1e8, 0x168};
-static const byte	default_irqs[MAX_HWIFS]     = {14, 15, 11, 10};
+static const int	default_irqs[MAX_HWIFS]     = {14, 15, 11, 10};
+#else
+static const byte	ide_hwif_to_major[MAX_HWIFS] = {IDE0_MAJOR};
+static const unsigned short default_io_base[MAX_HWIFS] = {0x1f0};
+static const int	default_irqs[MAX_HWIFS]     = {14};
+#endif
 static int	idebus_parameter; /* holds the "idebus=" parameter */
 static int	system_bus_speed; /* holds what we think is VESA/PCI bus speed */
 
@@ -366,7 +373,7 @@
 	/* fill in any non-zero initial values */
 	hwif->index     = index;
 	hwif->noprobe	= (index > 1);
-	hwif->io_base	= default_io_base[index];
+	hwif->io_base	= (ide_ioreg_t) default_io_base[index];
 	hwif->ctl_port	= hwif->io_base ? hwif->io_base+0x206 : 0x000;
 #ifdef CONFIG_BLK_DEV_HD
 	if (hwif->io_base == HD_DATA)
@@ -470,23 +477,21 @@
  */
 void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
 {
-	unsigned short io_base  = HWIF(drive)->io_base;
-	unsigned short data_reg = io_base+IDE_DATA_OFFSET;
 	byte io_32bit = drive->io_32bit;
 
 	if (io_32bit) {
 #if SUPPORT_VLB_SYNC
 		if (io_32bit & 2) {
 			cli();
-			do_vlb_sync(io_base+IDE_NSECTOR_OFFSET);
-			insl(data_reg, buffer, wcount);
+			do_vlb_sync(IDE_NSECTOR_REG);
+			insl(IDE_DATA_REG, buffer, wcount);
 			if (drive->unmask)
-				sti();
+				STI();
 		} else
 #endif /* SUPPORT_VLB_SYNC */
-			insl(data_reg, buffer, wcount);
+			insl(IDE_DATA_REG, buffer, wcount);
 	} else
-		insw(data_reg, buffer, wcount<<1);
+		insw(IDE_DATA_REG, buffer, wcount<<1);
 }
 
 /*
@@ -494,23 +499,21 @@
  */
 void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
 {
-	unsigned short io_base  = HWIF(drive)->io_base;
-	unsigned short data_reg = io_base+IDE_DATA_OFFSET;
 	byte io_32bit = drive->io_32bit;
 
 	if (io_32bit) {
 #if SUPPORT_VLB_SYNC
 		if (io_32bit & 2) {
 			cli();
-			do_vlb_sync(io_base+IDE_NSECTOR_OFFSET);
-			outsl(data_reg, buffer, wcount);
+			do_vlb_sync(IDE_NSECTOR_REG);
+			outsl(IDE_DATA_REG, buffer, wcount);
 			if (drive->unmask)
-				sti();
+				STI();
 		} else
 #endif /* SUPPORT_VLB_SYNC */
-			outsl(data_reg, buffer, wcount);
+			outsl(IDE_DATA_REG, buffer, wcount);
 	} else
-		outsw(data_reg, buffer, wcount<<1);
+		outsw(IDE_DATA_REG, buffer, wcount<<1);
 }
 
 /*
@@ -874,7 +877,7 @@
 	byte err = 0;
 
 	save_flags (flags);
-	sti();
+	STI();
 	printk("%s: %s: status=0x%02x", drive->name, msg, stat);
 #if FANCY_STATUS_DUMPS
 	if (drive->media == ide_disk) {
@@ -1168,7 +1171,7 @@
 {
 	byte stat = GET_STAT();
 
-	sti();
+	STI();
 	if (OK_STAT(stat,READY_STAT,BAD_STAT)) {
 		drive->mult_count = drive->mult_req;
 	} else {
@@ -1185,7 +1188,7 @@
 {
 	byte stat = GET_STAT();
 
-	sti();
+	STI();
 	if (!OK_STAT(stat,READY_STAT,BAD_STAT))
 		ide_error(drive, "set_geometry_intr", stat);
 }
@@ -1197,7 +1200,7 @@
 {
 	byte stat = GET_STAT();
 
-	sti();
+	STI();
 	if (!OK_STAT(stat,READY_STAT,BAD_STAT))
 		ide_error(drive, "recal_intr", stat);
 }
@@ -1211,7 +1214,7 @@
 	byte *args = (byte *) rq->buffer;
 	byte stat = GET_STAT();
 
-	sti();
+	STI();
 	if ((stat & DRQ_STAT) && args && args[3]) {
 		byte io_32bit = drive->io_32bit;
 		drive->io_32bit = 0;
@@ -1297,7 +1300,7 @@
 	}
 
 	save_flags(flags);
-	sti();
+	STI();
 	timeout += jiffies;
 	do {
 		if (!((stat = GET_STAT()) & BUSY_STAT)) {
@@ -1318,14 +1321,12 @@
  */
 static inline void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
 {
-	ide_hwif_t *hwif = HWIF(drive);
-	unsigned short io_base = hwif->io_base;
 #ifdef CONFIG_BLK_DEV_PROMISE
 	int use_promise_io = 0;
 #endif /* CONFIG_BLK_DEV_PROMISE */
 
 	OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
-	OUT_BYTE(rq->nr_sectors,io_base+IDE_NSECTOR_OFFSET);
+	OUT_BYTE(rq->nr_sectors,IDE_NSECTOR_REG);
 #ifdef CONFIG_BLK_DEV_PROMISE
 	if (IS_PROMISE_DRIVE) {
 		if (hwif->is_promise2 || rq->cmd == READ) {
@@ -1341,20 +1342,20 @@
 			drive->name, (rq->cmd==READ)?"read":"writ",
 			block, rq->nr_sectors, (unsigned long) rq->buffer);
 #endif
-		OUT_BYTE(block,io_base+IDE_SECTOR_OFFSET);
-		OUT_BYTE(block>>=8,io_base+IDE_LCYL_OFFSET);
-		OUT_BYTE(block>>=8,io_base+IDE_HCYL_OFFSET);
-		OUT_BYTE(((block>>8)&0x0f)|drive->select.all,io_base+IDE_SELECT_OFFSET);
+		OUT_BYTE(block,IDE_SECTOR_REG);
+		OUT_BYTE(block>>=8,IDE_LCYL_REG);
+		OUT_BYTE(block>>=8,IDE_HCYL_REG);
+		OUT_BYTE(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG);
 	} else {
 		unsigned int sect,head,cyl,track;
 		track = block / drive->sect;
 		sect  = block % drive->sect + 1;
-		OUT_BYTE(sect,io_base+IDE_SECTOR_OFFSET);
+		OUT_BYTE(sect,IDE_SECTOR_REG);
 		head  = track % drive->head;
 		cyl   = track / drive->head;
-		OUT_BYTE(cyl,io_base+IDE_LCYL_OFFSET);
-		OUT_BYTE(cyl>>8,io_base+IDE_HCYL_OFFSET);
-		OUT_BYTE(head|drive->select.all,io_base+IDE_SELECT_OFFSET);
+		OUT_BYTE(cyl,IDE_LCYL_REG);
+		OUT_BYTE(cyl>>8,IDE_HCYL_REG);
+		OUT_BYTE(head|drive->select.all,IDE_SELECT_REG);
 #ifdef DEBUG
 		printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n",
 			drive->name, (rq->cmd==READ)?"read":"writ", cyl,
@@ -1373,7 +1374,7 @@
 			return;
 #endif /* CONFIG_BLK_DEV_TRITON */
 		ide_set_handler(drive, &read_intr, WAIT_CMD);
-		OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, io_base+IDE_COMMAND_OFFSET);
+		OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG);
 		return;
 	}
 	if (rq->cmd == WRITE) {
@@ -1381,7 +1382,7 @@
 		if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive)))
 			return;
 #endif /* CONFIG_BLK_DEV_TRITON */
-		OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, io_base+IDE_COMMAND_OFFSET);
+		OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, IDE_COMMAND_REG);
 		if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
 			printk("%s: no DRQ after issuing %s\n", drive->name,
 				drive->mult_count ? "MULTWRITE" : "WRITE");
@@ -1440,7 +1441,7 @@
 	unsigned long block, blockend;
 	ide_drive_t *drive;
 
-	sti();
+	STI();
 #ifdef DEBUG
 	printk("%s: do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
 #endif
@@ -1537,6 +1538,116 @@
  * this problem (most don't), the unmask flag can be set using the "hdparm"
  * utility, to permit other interrupts during data/cmd transfers.
  */
+
+#if defined(CONFIG_ATARI)
+
+static void ide_intr (int, void *, struct  pt_regs *);
+
+/* for the new style (fairness) locking ... more sophisticated */
+/* The fairness-style locking code has been adapted from atari_scsi.c
+** (original implementation by Roman Hodek) ... bugs in the ide implementation 
+** are solely my fault :-)
+*/
+
+static int ide_got_lock = 0;
+static struct wait_queue	*ide_fairness_wait = NULL;
+#if 0
+static int ide_trying_lock = 0;
+static struct wait_queue	*ide_try_wait = NULL;
+#endif
+static int ide_dont_release = 0;
+
+/* This function releases the lock on the DMA chip if there is no
+ * currently executing command (as recognized by hwgroup->handler). On
+ * releasing, instances of ide_get_lock are awoken, that put
+ * themselves to sleep for fairness. They can now try to get the lock
+ * again (but others waiting longer more probably will win).
+ */
+
+static void
+ide_release_lock_if_possible( ide_hwgroup_t *hwgroup )
+{
+	unsigned long	oldflags;
+#if 0		
+	if (IS_A_TT()) return;
+#endif	
+	save_flags(oldflags);
+	cli();
+
+	if (ide_got_lock &&
+	        /* no need to care for different queues here ... */
+		!hwgroup->handler) {
+
+		if (ide_dont_release) {
+
+			printk("WARNING: Lock release not allowed. Ignored\n");
+
+			restore_flags(oldflags);
+			return;
+		}
+		ide_got_lock = 0;
+		stdma_release();
+		wake_up( &ide_fairness_wait );
+	}
+
+	restore_flags(oldflags);
+}
+
+/* This function manages the locking of the ST-DMA.
+ * If the DMA isn't locked already for IDE, it trys to lock it by
+ * calling stdma_lock(). But if the DMA is locked by the IDE code and
+ * there are other drivers waiting for the chip, we do not issue the
+ * command immediately but wait on 'ide_fairness_queue'. We will be
+ * waked up when the DMA is unlocked by some IDE interrupt. After that
+ * we try to get the lock again.
+ * But we must be prepared that more than one instance of
+ * ide_get_lock() is waiting on the fairness queue. They should not
+ * try all at once to call stdma_lock(), one is enough! For that, the
+ * first one sets 'ide_trying_lock', others that see that variable
+ * set wait on the queue 'ide_try_wait'.
+ * Complicated, complicated.... Sigh...
+ */
+
+static void ide_get_lock( ide_hwgroup_t *hwgroup )
+{
+	unsigned long	oldflags;
+#if 0
+	if (IS_A_TT()) return;
+#endif
+	save_flags(oldflags);
+	cli();
+
+	while( intr_count == 0 && ide_got_lock && stdma_others_waiting() )
+		sleep_on( &ide_fairness_wait );
+
+	while (!ide_got_lock) {
+		if (intr_count > 0)
+			panic( "Falcon IDE hasn't ST-DMA lock in interrupt" );
+#if 0
+		if (!ide_trying_lock) {
+			ide_trying_lock = 1;
+			/* This will possibly sleep ... */
+			stdma_lock(ide_intr, hwgroup);
+			ide_got_lock = 1;
+			ide_trying_lock = 0;
+			wake_up( &ide_try_wait );
+		}
+		else {
+			sleep_on( &ide_try_wait );
+		}
+#else
+		/* This will possibly sleep ... */
+		stdma_lock(ide_intr, hwgroup);
+		ide_got_lock = 1;
+#endif
+	}	
+
+	restore_flags(oldflags);
+	if (!ide_got_lock)
+		panic("Falcon IDE: someone stole the lock :-(\n");
+}
+#endif /* CONFIG_ATARI */
+
 void ide_do_request (ide_hwgroup_t *hwgroup)
 {
 	cli();	/* paranoia */
@@ -1544,6 +1655,19 @@
 		printk("%s: EEeekk!! handler not NULL in ide_do_request()\n", hwgroup->hwif->name);
 		return;
 	}
+#if defined(CONFIG_ATARI)
+/* MSch: On the Atari, it would be fatal if we didn't have the ST-DMA lock
+ * at this place. Either do_hwgroup_request() should have locked the
+ * DMA before (if we're called from ll_rw_blk.c), or we should own it
+ * for longer, if we're in an interrupt handler. This check is just to
+ * be sure... 
+ * Since without locking the DMA we cannot set the interrupt routine, this 
+ * should actually never happen!
+ */
+        if (MACH_IS_ATARI && !ide_got_lock) {
+	        printk("Weirdness: Falcon IDE hasn't ST-DMA lock!\n");
+	}
+#endif /* CONFIG_ATARI */
 	do {
 		ide_hwif_t *hwif = hwgroup->hwif;
 		struct request *rq;
@@ -1561,11 +1685,30 @@
 				if (rq != NULL && rq->rq_status != RQ_INACTIVE)
 					goto got_rq;
 			} while ((hwif = hwif->next) != hwgroup->next_hwif);
+#if defined(CONFIG_ATARI)
+			/* MSch: On the Atari, we are about to leave, with all 
+			 * requests finished and nothing else to do at this
+			 * place.
+			 * So we must give up the DMA lock here, for use by the 
+			 * SCSI/ACSI/floppy driver.
+			 */
+			if (MACH_IS_ATARI) {
+			        ide_dont_release = 0;
+				ide_release_lock_if_possible(hwgroup);
+			}
+#endif /* CONFIG_ATARI */
 			return;		/* no work left for this hwgroup */
 		}
 	got_rq:	
 		do_request(hwgroup->hwif = hwgroup->next_hwif = hwif, hwgroup->rq = rq);
 		cli();
+#if defined(CONFIG_ATARI)
+		/* optional release-if-possible here ?? */
+		if (MACH_IS_ATARI) {
+		        ide_release_lock_if_possible(hwgroup);
+			if (!ide_got_lock) return; /* ?? */
+		}
+#endif /* CONFIG_ATARI */
 	} while (hwgroup->handler == NULL);
 }
 
@@ -1585,6 +1728,22 @@
 	if (hwgroup->handler == NULL) {
 		ide_hwif_t *hgif = hwgroup->hwif;
 		ide_hwif_t *hwif = hgif;
+
+#if defined(CONFIG_ATARI)
+/* MSch: On the Atari, this is the place to lock the ST-DMA, since
+ * do_hwgroup_request() is only called by ll_rw_blk.c, thus in the
+ * context of a process and not from an interrupt. This condition is
+ * necessary, because stdma_lock() sleeps if somebody else already
+ * owns the lock. And the next inner function, do_ide_request(), can
+ * also be called from an int handler (that may never sleep).
+ * Note also that the locking must be done before disable_irq(), else
+ * we would prevent ST-DMA ints being delivered, even in case somebody
+ * else currently owns the lock.
+ */
+		if (MACH_IS_ATARI) {
+		  ide_get_lock(hwgroup);
+		}
+#endif /* CONFIG_ATARI */
 		do {
 			disable_irq(hwif->irq);
 		} while ((hwif = hwif->next) != hgif);
@@ -1635,7 +1794,7 @@
 		hwgroup->handler = NULL;
 		handler(drive);
 	} else if (hwgroup->handler == NULL) {	 /* not waiting for anything? */
-		sti(); /* drive must have responded just as the timer expired */
+		STI(); /* drive must have responded just as the timer expired */
 		printk("%s: marginal timeout\n", drive->name);
 	} else {
 		hwgroup->handler = NULL;	/* abort the operation */
@@ -1713,6 +1872,20 @@
 	ide_hwgroup_t *hwgroup = dev_id;
 	ide_handler_t *handler;
 
+#ifdef CONFIG_AMIGA
+	if (MACH_IS_AMIGA) {
+		unsigned char ch;
+		ide_drive_t *drive = hwgroup->drive;
+               	ch = inb(IDE_IRQ_REG);
+		if (!(ch & 0x80))
+			return;
+		if (AMIGAHW_PRESENT(A1200_IDE)) {
+			(void) GET_STAT();
+			outb(0x7c | (ch & 0x03), IDE_IRQ_REG);
+		}
+	}
+#endif /* CONFIG_AMIGA */
+
 	if (irq == hwgroup->hwif->irq && (handler = hwgroup->handler) != NULL) {
 		ide_drive_t *drive = hwgroup->drive;
 		hwgroup->handler = NULL;
@@ -1723,7 +1896,15 @@
 		cli();	/* this is necessary, as next rq may be different irq */
 		if (hwgroup->handler == NULL) {
 			SET_RECOVERY_TIMER(HWIF(drive));
- 			ide_do_request(hwgroup);
+#if defined(CONFIG_ATARI)
+			/* not sure about these ... */
+			ide_dont_release++;
+#endif
+			ide_do_request(hwgroup);
+#if defined(CONFIG_ATARI)
+			if (ide_dont_release) 
+			  ide_dont_release--;
+#endif
 		}
 	} else {
 		unexpected_intr(irq, hwgroup);
@@ -2250,7 +2431,11 @@
 
 	id = drive->id = kmalloc (SECTOR_WORDS*4, GFP_KERNEL);
 	ide_input_data(drive, id, SECTOR_WORDS);	/* read 512 bytes of id info */
-	sti();
+#if defined(CONFIG_AMIGA)
+        if (MACH_IS_AMIGA)
+		big_endianize_driveid(id);
+#endif /* CONFIG_AMIGA */
+	STI();
 
 	/*
 	 * EATA SCSI controllers do a hardware ATA emulation:  ignore them
@@ -2266,12 +2451,16 @@
 	 *  WIN_IDENTIFY returns little-endian info,
 	 *  WIN_PIDENTIFY *usually* returns little-endian info.
 	 */
+#ifdef __BIG_ENDIAN
+	bswap = 0;
+#else
 	bswap = 1;
+#endif
 	if (cmd == WIN_PIDENTIFY) {
 		if ((id->model[0] == 'N' && id->model[1] == 'E') /* NEC */
 		 || (id->model[0] == 'F' && id->model[1] == 'X') /* Mitsumi */
 		 || (id->model[0] == 'P' && id->model[1] == 'i'))/* Pioneer */
-			bswap = 0;	/* Vertos drives may still be weird */
+			bswap ^= 1;	/* Vertos drives may still be weird */
 	}
 	ide_fixstring (id->model,     sizeof(id->model),     bswap);
 	ide_fixstring (id->fw_rev,    sizeof(id->fw_rev),    bswap);
@@ -2444,10 +2633,15 @@
  */
 static int try_to_identify (ide_drive_t *drive, byte cmd)
 {
-	int hd_status, rc;
+	int rc;
 	unsigned long timeout;
+	ide_ioreg_t hd_status;
 	int irqs = 0;
 
+#ifdef __mc68000__
+	OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG);
+#endif
+
 	if (!HWIF(drive)->irq) {		/* already got an IRQ? */
 		probe_irq_off(probe_irq_on());	/* clear dangling irqs */
 		irqs = probe_irq_on();		/* start monitoring irqs */
@@ -2565,6 +2759,10 @@
 			rc = try_to_identify(drive,cmd); /* failed: try again */
 		if (rc == 1)
 			printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT());
+#ifdef __mc68000__
+		OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG);
+		delay_50ms();
+#endif /* __mc68000__ */
 		(void) GET_STAT();		/* ensure drive irq is clear */
 	} else {
 		rc = 3;				/* not present or maybe ATAPI */
@@ -2572,6 +2770,10 @@
 	if (drive->select.b.unit != 0) {
 		OUT_BYTE(0xa0,IDE_SELECT_REG);	/* exit with drive0 selected */
 		delay_50ms();
+#ifdef __mc68000__
+		OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG);
+		delay_50ms();
+#endif /* __mc68000__ */
 		(void) GET_STAT();		/* ensure drive irq is clear */
 	}
 	return rc;
@@ -2661,6 +2863,31 @@
 #endif
 }
 
+#ifdef __mc68000__
+/*
+ * Some weird drives (Maxtor, Western Digital) lock up after identification
+ */
+static int post_ident_reset = 0;
+
+static const char *pir_blacklist[] = {
+    /* For partial string compare */
+    "Maxtor 7540 AV",	/* Thorsten Floeck <floeck@wctc6.chemie.uni-wuppertal.de> */
+    "WDC AC2340H",	/* Lars Balker Rasmussen <gnort@daimi.aau.dk> */
+    NULL
+};
+
+static int scan_pir_blacklist(const char *model)
+{
+    const char **p;
+
+    for (p = pir_blacklist; *p; p++) {
+	if (!strncmp(*p, model, strlen(*p)))
+	    return(1);
+    }
+    return(0);
+}
+#endif /* __mc68000__ */
+
 /*
  * This routine only knows how to look for drive units 0 and 1
  * on an interface, so any setting of MAX_DRIVES > 2 won't work here.
@@ -2673,6 +2900,7 @@
 		return;
 	if (hwif->io_base == HD_DATA)
 		probe_cmos_for_drives (hwif);
+#ifdef IDE_IO_PORT_IO
 #if CONFIG_BLK_DEV_PROMISE
 	if (!hwif->is_promise2 &&
 	   (check_region(hwif->io_base,8) || check_region(hwif->ctl_port,1))) {
@@ -2690,11 +2918,13 @@
 		}
 		if (!msgout)
 			printk("%s: ports already in use, skipping probe\n", hwif->name);
-	} else {
+	} else
+#endif /* IDE_IO_PORT_IO */
+	{
 		unsigned long flags;
 		save_flags(flags);
 
-		sti();	/* needed for jiffies and irq probing */
+		STI();	/* needed for jiffies and irq probing */
 		/*
 		 * Second drive should only exist if first drive was found,
 		 * but a lot of cdrom drives are configured as single slaves.
@@ -2708,11 +2938,23 @@
 					 drive->name, drive->head);
 					drive->present = 0;
 				}
+#ifdef __mc68000__
+				/* Is this a weird drive that locks up after
+				   identification? */
+				if (scan_pir_blacklist(drive->id->model)) {
+				    printk("%s: This drive is on our post "
+					   "identification reset blacklist.\n",
+					   drive->name);
+				    post_ident_reset = 1;
+				}
+#endif /* __mc68000__ */
 			}
 			if (drive->present && !hwif->present) {
 				hwif->present = 1;
+#ifdef IDE_IO_PORT_IO
 				request_region(hwif->io_base,  8, hwif->name);
 				request_region(hwif->ctl_port, 1, hwif->name);
+#endif
 			}
 		}
 		restore_flags(flags);
@@ -3032,8 +3274,8 @@
 			case 2: /* base,ctl */
 				vals[2] = 0;	/* default irq = probe for it */
 			case 3: /* base,ctl,irq */
-				hwif->io_base  = vals[0];
-				hwif->ctl_port = vals[1];
+				hwif->io_base  = (ide_ioreg_t) vals[0];
+				hwif->ctl_port = (ide_ioreg_t) vals[1];
 				hwif->irq      = vals[2];
 				hwif->noprobe  = 0;
 				hwif->chipset  = ide_generic;
@@ -3221,6 +3463,16 @@
 		hwgroup->timer.data = (unsigned long) hwgroup;
 	}
 
+#ifdef __mc68000__
+#ifdef CONFIG_AMIGA
+	if (MACH_IS_AMIGA){
+		if (request_irq(hwif->irq, ide_intr, 0, hwif->name, hwgroup)) {
+			restore_flags(flags);
+			return 1;
+		}
+	}
+#endif /* CONFIG_AMIGA */
+#else /* __mc68000__ */
 	/*
 	 * Allocate the irq, if not already obtained for another hwif
 	 */
@@ -3232,6 +3484,7 @@
 			return 1;
 		}
 	}
+#endif /* __mc68000__ */
 
 	/*
 	 * Everything is okay, so link us into the hwgroup
@@ -3242,10 +3495,12 @@
 
 	restore_flags(flags);	/* safe now that hwif->hwgroup is set up */
 
+#ifdef IDE_IO_PORT_IO
 	printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", hwif->name,
 		hwif->io_base, hwif->io_base+7, hwif->ctl_port, hwif->irq);
 	if (match)
 		printk(" (%sed with %s)", hwif->sharing_irq ? "shar" : "serializ", match->name);
+#endif
 	printk("\n");
 	return 0;
 }
@@ -3292,6 +3547,70 @@
 #endif /* defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) */
 #endif /* CONFIG_PCI */
 
+#ifdef CONFIG_AMIGA
+static void ide_probe_amiga (void)
+{
+	ide_hwif_t *hwif = &ide_hwifs[0];
+
+	if (!MACH_IS_AMIGA)
+		return;
+
+/* Test if an A4000 or an A1200 and setup the base of hd_regs acordingly */
+	hwif->irq = IRQ_AMIGA_PORTS;
+
+	if (AMIGAHW_PRESENT(A4000_IDE)) {
+		hwif->io_base = (unsigned char *)ZTWO_VADDR(HD_BASE_A4000);
+		hwif->hd_regs.hd_irq = (unsigned char *)ZTWO_VADDR(HD_A4000_IRQ);
+	} else if (AMIGAHW_PRESENT(A1200_IDE)) {
+		hwif->io_base = (unsigned char *)ZTWO_VADDR(HD_BASE_A1200);
+		hwif->hd_regs.hd_irq = (unsigned char *)ZTWO_VADDR(HD_A1200_IRQ);
+	} else /* Not A4000 nor A1200, 
+		  doesn't have a supported IDE controller */
+		{
+			printk ("Gayle IDE harddisk controller not present.\n");
+			hwif->noprobe = 1;
+			return;
+		}
+
+	/* Now set the hd_regs struct */
+	hwif->hd_regs.hd_error   = hwif->io_base + AMI_HD_ERROR;
+	hwif->hd_regs.hd_nsector = hwif->io_base + AMI_HD_NSECTOR;
+	hwif->hd_regs.hd_sector  = hwif->io_base + AMI_HD_SECTOR;
+	hwif->hd_regs.hd_lcyl    = hwif->io_base + AMI_HD_LCYL;
+	hwif->hd_regs.hd_hcyl    = hwif->io_base + AMI_HD_HCYL;
+	hwif->hd_regs.hd_select  = hwif->io_base + AMI_HD_SELECT;
+	hwif->hd_regs.hd_status  = hwif->io_base + AMI_HD_STATUS;
+	hwif->ctl_port           = hwif->io_base + AMI_HD_CMD;
+}
+#endif /* CONFIG_AMIGA */
+
+#ifdef CONFIG_ATARI
+static void ide_probe_atari (void)
+{
+	ide_hwif_t *hwif = &ide_hwifs[0];
+
+	if (!MACH_IS_ATARI)
+		return;
+
+	if (!ATARIHW_PRESENT(IDE)) {
+		hwif->noprobe = 1;
+		return;
+	}
+	hwif->irq = IRQ_MFP_IDE;
+	hwif->io_base = (unsigned char *)ATA_HD_BASE;
+
+	/* Now set the hd_regs struct */
+	hwif->hd_regs.hd_error     = hwif->io_base + ATA_HD_ERROR;
+	hwif->hd_regs.hd_nsector   = hwif->io_base + ATA_HD_NSECTOR;
+	hwif->hd_regs.hd_sector    = hwif->io_base + ATA_HD_SECTOR;
+	hwif->hd_regs.hd_lcyl      = hwif->io_base + ATA_HD_LCYL;
+	hwif->hd_regs.hd_hcyl      = hwif->io_base + ATA_HD_HCYL;
+	hwif->hd_regs.hd_select    = hwif->io_base + ATA_HD_CURRENT;
+	hwif->hd_regs.hd_status    = hwif->io_base + ATA_HD_STATUS;
+	hwif->ctl_port             = hwif->io_base + ATA_HD_CMD;
+}
+#endif /* CONFIG_ATARI */
+
 /*
  * ide_init_pci() finds/initializes "known" PCI IDE interfaces
  *
@@ -3331,6 +3650,12 @@
 #ifdef CONFIG_BLK_DEV_PROMISE
 	init_dc4030();
 #endif
+#ifdef CONFIG_AMIGA
+	ide_probe_amiga();
+#endif
+#ifdef CONFIG_ATARI
+	ide_probe_atari();
+#endif
 }
 
 static int hwif_init (int h)
@@ -3380,6 +3705,13 @@
 		read_ahead[hwif->major] = 8;	/* (4kB) */
 		hwif->present = 1;	/* success */
 	}
+#ifdef __mc68000__
+	if (post_ident_reset) {
+	    printk("ide: Doing a post identification reset...\n");
+	    outb(4,hwif->ctl_port);
+	    outb(0,hwif->ctl_port);
+	}
+#endif /* __mc68000__ */
 	return hwif->present;
 }
 
@@ -3482,6 +3814,7 @@
 	if (irq_count == 1)
 		free_irq(hwif->irq, hwgroup);
 
+#ifdef IDE_IO_PORT_IO
 	/*
 	 * Note that we only release the standard ports,
 	 * and do not even try to handle any extra ports
@@ -3489,7 +3822,7 @@
 	 */
 	release_region(hwif->io_base, 8);
 	release_region(hwif->ctl_port, 1);
-
+#endif
 	/*
 	 * Remove us from the hwgroup, and free
 	 * the hwgroup if we were the only member
diff -urN pc-2.0.21/drivers/block/ide.h linux-2.0.21/drivers/block/ide.h
--- pc-2.0.21/drivers/block/ide.h	Fri Sep 20 14:21:40 1996
+++ linux-2.0.21/drivers/block/ide.h	Thu Sep 26 22:37:02 1996
@@ -5,6 +5,7 @@
  */
 
 #include <linux/config.h>
+#include <asm/ide.h>
 
 /*
  * This is the multiple IDE interface driver, as evolved from hd.c.  
@@ -84,6 +85,10 @@
 #define HWIF(drive)		((ide_hwif_t *)((drive)->hwif))
 #define HWGROUP(drive)		((ide_hwgroup_t *)(HWIF(drive)->hwgroup))
 
+#ifdef IDE_IO_PORT_IO
+/*
+ * io-port-io specific definitions for accessing IDE controller registers
+ */
 #define IDE_DATA_OFFSET		(0)
 #define IDE_ERROR_OFFSET	(1)
 #define IDE_NSECTOR_OFFSET	(2)
@@ -108,6 +113,25 @@
 #define IDE_COMMAND_REG		IDE_STATUS_REG
 #define IDE_ALTSTATUS_REG	IDE_CONTROL_REG
 
+#else /* then IDE_MEM_MAPPED_IO */
+/*
+ * mem-mapped-io specific definitions for accessing IDE controller registers
+ */
+#define IDE_DATA_REG		(HWIF(drive)->io_base)
+#define IDE_ERROR_REG		(HWIF(drive)->hd_regs.hd_error)
+#define IDE_NSECTOR_REG		(HWIF(drive)->hd_regs.hd_nsector)
+#define IDE_SECTOR_REG		(HWIF(drive)->hd_regs.hd_sector)
+#define IDE_LCYL_REG		(HWIF(drive)->hd_regs.hd_lcyl)
+#define IDE_HCYL_REG		(HWIF(drive)->hd_regs.hd_hcyl)
+#define IDE_SELECT_REG		(HWIF(drive)->hd_regs.hd_select)
+#define IDE_STATUS_REG		(HWIF(drive)->hd_regs.hd_status)
+#define IDE_CONTROL_REG		(HWIF(drive)->ctl_port)
+#define IDE_FEATURE_REG		IDE_ERROR_REG
+#define IDE_COMMAND_REG		IDE_STATUS_REG
+#define IDE_ALTSTATUS_REG	IDE_CONTROL_REG
+#define IDE_IRQ_REG		(HWIF(drive)->hd_regs.hd_irq)
+#endif
+
 #ifdef REALLY_FAST_IO
 #define OUT_BYTE(b,p)		outb((b),(p))
 #define IN_BYTE(p)		(byte)inb(p)
@@ -133,9 +157,6 @@
 #define PARTN_BITS	6	/* number of minor dev bits for partitions */
 #define PARTN_MASK	((1<<PARTN_BITS)-1)	/* a useful bit mask */
 #define MAX_DRIVES	2	/* per interface; 2 assumed by lots of code */
-#ifndef MAX_HWIFS
-#define MAX_HWIFS	4	/* an arbitrary, but realistic limit */
-#endif
 #define SECTOR_WORDS	(512 / 4)	/* number of 32bit words per sector */
 
 /*
@@ -151,6 +172,7 @@
 #define WAIT_WORSTCASE	(30*HZ)	/* 30sec  - worst case when spinning up */
 #define WAIT_CMD	(10*HZ)	/* 10sec  - maximum wait for an IRQ to happen */
 
+#ifdef IDE_IO_PORT_IO
 #if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PROMISE)
 #define SELECT_DRIVE(hwif,drive)				\
 {								\
@@ -162,6 +184,9 @@
 #else
 #define SELECT_DRIVE(hwif,drive)  OUT_BYTE((drive)->select.all, hwif->io_base+IDE_SELECT_OFFSET);
 #endif	/* CONFIG_BLK_DEV_HT6560B || CONFIG_BLK_DEV_PROMISE */
+#else /* IDE_IO_PORT_IO vs IDE_MEM_MAPPED_IO */
+#define SELECT_DRIVE(hwif,drive)  OUT_BYTE((drive)->select.all, hwif->hd_regs.hd_select);
+#endif /* IDE_IO_PORT_IO */
 		
 #ifdef CONFIG_BLK_DEV_IDETAPE
 #include "ide-tape.h"
@@ -303,6 +328,18 @@
 		} b;
 	} special_t;
 
+#ifdef __BIG_ENDIAN_BITFIELD
+typedef union {
+	unsigned all			: 8;	/* all of the bits together */
+	struct {
+		unsigned bit7		: 1;	/* always 1 */
+		unsigned lba		: 1;	/* using LBA instead of CHS */
+		unsigned bit5		: 1;	/* always 1 */
+		unsigned unit		: 1;	/* drive select number, 0 or 1 */
+		unsigned head		: 4;	/* always zeros here */
+	} b;
+	} select_t;
+#else /* __BIG_ENDIAN_BITFIELD */
 typedef union {
 	unsigned all			: 8;	/* all of the bits together */
 	struct {
@@ -313,6 +350,7 @@
 		unsigned bit7		: 1;	/* always 1 */
 	} b;
 	} select_t;
+#endif /* __BIG_ENDIAN_BITFIELD */
 
 typedef struct ide_drive_s {
 	special_t	special;	/* special action flags */
@@ -412,8 +450,18 @@
 typedef struct hwif_s {
 	struct hwif_s	*next;		/* for linked-list in ide_hwgroup_t */
 	void		*hwgroup;	/* actually (ide_hwgroup_t *) */
+#ifdef IDE_MEM_MAPPED_IO
+	unsigned char *	io_base;	/* base addres for port */
+	unsigned char *	ctl_port;	/* The control port */
+	struct {
+		unsigned char *hd_error, *hd_nsector, *hd_sector,
+		              *hd_lcyl, *hd_hcyl, *hd_select,
+		              *hd_status, *hd_irq;
+	} hd_regs;
+#else /* IDE_IO_PORT_IO */
 	unsigned short	io_base;	/* base io port addr */
 	unsigned short	ctl_port;	/* usually io_base+0x206 */
+#endif
 	ide_drive_t	drives[MAX_DRIVES];	/* drive info */
 	struct gendisk	*gd;		/* gendisk structure */
 	ide_tuneproc_t	*tuneproc;	/* routine to tune PIO mode for drives */
@@ -423,10 +471,10 @@
 	ide_dmaproc_t	*dmaproc;	/* dma read/write/abort routine */
 	unsigned long	*dmatable;	/* dma physical region descriptor table */
 	unsigned short	dma_base;	/* base addr for dma ports (triton) */
-	byte		irq;		/* our irq number */
+	int		irq;		/* our irq number */
 	byte		major;		/* our major number */
-	char 		name[5];	/* name of interface, eg. "ide0" */
 	byte		index;		/* 0 for ide0; 1 for ide1; ... */
+	char 		name[6];	/* name of interface, eg. "ide0" */
 	hwif_chipset_t	chipset;	/* sub-module for tuning.. */
 	unsigned	noprobe    : 1;	/* don't probe for this interface */
 	unsigned	present    : 1;	/* this interface exists */
diff -urN pc-2.0.21/drivers/block/ll_rw_blk.c linux-2.0.21/drivers/block/ll_rw_blk.c
--- pc-2.0.21/drivers/block/ll_rw_blk.c	Fri Sep 20 11:50:55 1996
+++ linux-2.0.21/drivers/block/ll_rw_blk.c	Thu Sep 26 19:20:25 1996
@@ -369,6 +369,7 @@
 	     case FLOPPY_MAJOR:
 	     case IDE2_MAJOR:
 	     case IDE3_MAJOR:
+	     case ACSI_MAJOR:
 		/*
 		 * The scsi disk and cdrom drivers completely remove the request
 		 * from the queue when they start processing an entry.  For this
@@ -628,6 +629,9 @@
 		req->next = NULL;
 	}
 	memset(ro_bits,0,sizeof(ro_bits));
+#ifdef CONFIG_AMIGA_Z2RAM
+	z2_init();
+#endif
 #ifdef CONFIG_BLK_DEV_RAM
 	rd_init();
 #endif
@@ -649,11 +653,16 @@
 #ifdef CONFIG_BLK_DEV_FD
 	floppy_init();
 #else
+#if !defined (__mc68000__)
 	outb_p(0xc, 0x3f2);
 #endif
+#endif
 #ifdef CONFIG_CDU31A
 	cdu31a_init();
 #endif CONFIG_CDU31A
+#ifdef CONFIG_ATARI_ACSI
+	acsi_init();
+#endif CONFIG_ATARI_ACSI
 #ifdef CONFIG_MCD
 	mcd_init();
 #endif CONFIG_MCD
diff -urN pc-2.0.21/drivers/block/z2ram.c linux-2.0.21/drivers/block/z2ram.c
--- pc-2.0.21/drivers/block/z2ram.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/block/z2ram.c	Sat Aug 24 14:33:34 1996
@@ -0,0 +1,354 @@
+/*
+** z2ram - Amiga pseudo-driver to access 16bit-RAM in ZorroII space
+**         as a block device, to be used as a RAM disk or swap space
+** 
+** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
+**
+** ++Geert: support for zorro_unused_z2ram, better range checking
+** ++roman: translate accesses via an array
+** ++Milan: support for ChipRAM usage
+** ++yambo: converted to 2.0 kernel
+** ++yambo: modularized and support added for 3 minor devices including:
+**          MAJOR  MINOR  DESCRIPTION
+**          -----  -----  ----------------------------------------------
+**          37     0       Use Zorro II and Chip ram
+**          37     1       Use only Zorro II ram
+**          37     2       Use only Chip ram
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#define MAJOR_NR    Z2RAM_MAJOR
+
+#include <linux/major.h>
+#include <linux/malloc.h>
+#include <linux/blk.h>
+
+#if defined(MODULE)
+#include <linux/module.h>
+#endif
+
+#include <asm/setup.h>
+#include <asm/bitops.h>
+#include <asm/amigahw.h>
+#include <asm/zorro.h>
+
+#define TRUE                  (1)
+#define FALSE                 (0)
+
+#define Z2MINOR_COMBINED      (0)
+#define Z2MINOR_Z2ONLY        (1)
+#define Z2MINOR_CHIPONLY      (2)
+
+#define Z2RAM_CHUNK1024       ( Z2RAM_CHUNKSIZE >> 10 )
+
+static u_long *z2ram_map    = NULL;
+static u_long z2ram_size    = 0;
+static int z2_blocksizes[3] = { 1024, 1024, 1024 };
+static int z2_sizes[3]      = {    0,    0,    0 };
+static int z2_count         = 0;
+static int chip_count       = 0;
+static int current_device   = -1;
+
+static void
+do_z2_request( void )
+{
+    u_long start, len, addr, size;
+
+    while ( TRUE )
+    {
+        INIT_REQUEST;
+
+        start = CURRENT->sector << 9;
+        len  = CURRENT->current_nr_sectors << 9;
+
+        if ( ( start + len ) > z2ram_size )
+        {
+            printk( KERN_ERR DEVICE_NAME ": bad access: block=%ld, count=%ld\n",
+                CURRENT->sector,
+                CURRENT->current_nr_sectors);
+            end_request( FALSE );
+            continue;
+        }
+
+        if ( ( CURRENT->cmd != READ ) && ( CURRENT->cmd != WRITE ) )
+        {
+            printk( KERN_ERR DEVICE_NAME ": bad command: %d\n", CURRENT->cmd );
+            end_request( FALSE );
+            continue;
+        }
+
+        while ( len ) 
+        {
+            addr = start & Z2RAM_CHUNKMASK;
+            size = Z2RAM_CHUNKSIZE - addr;
+            if ( len < size )
+                size = len;
+
+            addr += z2ram_map[ start >> Z2RAM_CHUNKSHIFT ];
+
+            if ( CURRENT->cmd == READ )
+                memcpy( CURRENT->buffer, (char *)addr, size );
+            else
+                memcpy( (char *)addr, CURRENT->buffer, size );
+
+            start += size;
+            len -= size;
+        }
+
+        end_request( TRUE );
+    }
+}
+
+static void
+get_z2ram( void )
+{
+    int i;
+
+    for ( i = 0; i < Z2RAM_SIZE / Z2RAM_CHUNKSIZE; i++ )
+    {
+        if ( test_bit( i, zorro_unused_z2ram ) )
+        {
+            z2_count++;
+            z2ram_map[ z2ram_size++ ] = 
+                ZTWO_VADDR( Z2RAM_START ) + ( i << Z2RAM_CHUNKSHIFT );
+            clear_bit( i, zorro_unused_z2ram );
+        }
+    }
+
+    return;
+}
+
+static void
+get_chipram( void )
+{
+
+    while ( amiga_chip_avail() > ( Z2RAM_CHUNKSIZE * 4 ) )
+    {
+        chip_count++;
+        z2ram_map[ z2ram_size ] =
+            (u_long)amiga_chip_alloc( Z2RAM_CHUNKSIZE );
+
+        if ( z2ram_map[ z2ram_size ] == 0 )
+        {
+            break;
+        }
+
+        z2ram_size++;
+    }
+        
+    return;
+}
+
+static int
+z2_open( struct inode *inode, struct file *filp )
+{
+    int device;
+    int max_z2_map = ( Z2RAM_SIZE / Z2RAM_CHUNKSIZE ) *
+        sizeof( z2ram_map[0] );
+    int max_chip_map = ( boot_info.bi_amiga.chip_size / Z2RAM_CHUNKSIZE ) *
+        sizeof( z2ram_map[0] );
+
+    device = DEVICE_NR( inode->i_rdev );
+
+    if ( current_device != -1 && current_device != device )
+    {
+        return -EBUSY;
+    }
+
+    if ( current_device == -1 )
+    {
+        z2_count   = 0;
+        chip_count = 0;
+        z2ram_size = 0;
+
+        switch ( device )
+        {
+            case Z2MINOR_COMBINED:
+
+                z2ram_map = kmalloc( max_z2_map + max_chip_map, GFP_KERNEL );
+                if ( z2ram_map == NULL )
+                {
+                    printk( KERN_ERR DEVICE_NAME
+                        ": cannot get mem for z2ram_map\n" );
+                    return -ENOMEM;
+                }
+
+                get_z2ram();
+                get_chipram();
+
+                if ( z2ram_size != 0 )
+                    printk( KERN_INFO DEVICE_NAME 
+                        ": using %iK Zorro II RAM and %iK Chip RAM (Total %dK)\n",
+                        z2_count * Z2RAM_CHUNK1024,
+                        chip_count * Z2RAM_CHUNK1024,
+                        ( z2_count + chip_count ) * Z2RAM_CHUNK1024 );
+
+            break;
+
+    	    case Z2MINOR_Z2ONLY:
+                z2ram_map = kmalloc( max_z2_map, GFP_KERNEL );
+                if ( z2ram_map == NULL )
+                {
+                    printk( KERN_ERR DEVICE_NAME
+                        ": cannot get mem for z2ram_map\n" );
+                    return -ENOMEM;
+                }
+
+                get_z2ram();
+
+                if ( z2ram_size != 0 )
+                    printk( KERN_INFO DEVICE_NAME 
+                        ": using %iK of Zorro II RAM\n",
+                        z2_count * Z2RAM_CHUNK1024 );
+
+            break;
+
+            case Z2MINOR_CHIPONLY:
+                z2ram_map = kmalloc( max_chip_map, GFP_KERNEL );
+                if ( z2ram_map == NULL )
+                {
+                    printk( KERN_ERR DEVICE_NAME
+                        ": cannot get mem for z2ram_map\n" );
+                    return -ENOMEM;
+                }
+
+                get_chipram();
+
+                if ( z2ram_size != 0 )
+                    printk( KERN_INFO DEVICE_NAME 
+                        ": using %iK Chip RAM\n",
+                        chip_count * Z2RAM_CHUNK1024 );
+                    
+            break;
+
+            default:
+                return -ENODEV;
+        }
+
+        if ( z2ram_size == 0 )
+        {
+            kfree( z2ram_map );
+            printk( KERN_NOTICE DEVICE_NAME
+                ": no unused ZII/Chip RAM found\n" );
+            return -ENOMEM;
+        }
+
+        current_device = device;
+        z2ram_size <<= Z2RAM_CHUNKSHIFT;
+        z2_sizes[ device ] = z2ram_size >> 10;
+        blk_size[ MAJOR_NR ] = z2_sizes;
+    }
+
+#if defined(MODULE)
+    MOD_INC_USE_COUNT;
+#endif
+
+    return 0;
+}
+
+static void 
+z2_release( struct inode *inode, struct file *filp )
+{
+
+    if ( current_device == -1 )
+        return;     
+
+    sync_dev( inode->i_rdev );
+
+#if defined(MODULE)
+    MOD_DEC_USE_COUNT;
+#endif
+
+    return;
+}
+
+static struct file_operations z2_fops =
+{
+        NULL,                   /* lseek - default */
+        block_read,             /* read - general block-dev read */
+        block_write,            /* write - general block-dev write */
+        NULL,                   /* readdir - bad */
+        NULL,                   /* select */
+        NULL,                   /* ioctl */
+        NULL,                   /* mmap */
+        z2_open,                /* open */
+        z2_release,             /* release */
+        block_fsync             /* fsync */
+};
+
+int
+z2_init( void )
+{
+
+    if ( !MACH_IS_AMIGA )
+        return -ENXIO;
+
+    if ( register_blkdev( MAJOR_NR, DEVICE_NAME, &z2_fops ) )
+    {
+        printk( KERN_ERR DEVICE_NAME ": Unable to get major %d\n",
+            MAJOR_NR );
+        return -EBUSY;
+    }
+   
+    blk_dev[ MAJOR_NR ].request_fn = DEVICE_REQUEST;
+    blksize_size[ MAJOR_NR ] = z2_blocksizes;
+    blk_size[ MAJOR_NR ] = z2_sizes;
+
+    return 0;
+}
+
+#if defined(MODULE)
+int
+init_module( void )
+{
+    int error;
+    
+    error = z2_init();
+    if ( error == 0 )
+    {
+        printk( KERN_INFO DEVICE_NAME ": loaded as module\n" );
+    }
+    
+    return error;
+}
+
+void
+cleanup_module( void )
+{
+    int i, j;
+
+    if ( unregister_blkdev( MAJOR_NR, DEVICE_NAME ) != 0 )
+        printk( KERN_ERR DEVICE_NAME ": unregister of device failed\n");
+
+    if ( current_device != -1 )
+    {
+        i = 0;
+
+        for ( j = 0 ; j < z2_count; j++ )
+        {
+            set_bit( i++, zorro_unused_z2ram ); 
+        }
+
+        for ( j = 0 ; j < chip_count; j++ )
+        {
+            if ( z2ram_map[ i ] )
+            {
+                amiga_chip_free( (void *) z2ram_map[ i++ ] );
+            }
+        }
+
+        if ( z2ram_map != NULL )
+        {
+            kfree( z2ram_map );
+        }
+    }
+
+    return;
+} 
+#endif
diff -urN pc-2.0.21/drivers/char/16c552.h linux-2.0.21/drivers/char/16c552.h
--- pc-2.0.21/drivers/char/16c552.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/16c552.h	Sat Aug 24 14:33:51 1996
@@ -0,0 +1,181 @@
+/*
+ * Definitions for the 16c552 DACE
+ * (dual-asynchronous-communications-element) used on the GVP
+ * IO-Extender. 
+ *
+ * Basically this is two 16c550 uarts's and a parallel port, which is
+ * why the serial definitions should be valid for the 16c550 uart
+ * aswell.
+ *
+ * Data was taken from National Semiconductors duart 16c552
+ * data-sheets and the Texas Instruments DACE 16c552 data-sheets (the
+ * NS version of the chip is _non_ standard and their data-sheets did
+ * cost me several wasted hours of work).
+ *
+ * This file is (C) 1995 Jes Sorensen (jds@kom.auc.dk)
+ */
+
+#ifndef _16C552_H_
+#define _16C552_H_
+
+/* Serial stuff */
+
+struct uart_16c550 {
+  volatile u_char skip0;
+  volatile u_char RBR;
+  volatile u_char skip1;
+  volatile u_char IER;
+  volatile u_char skip2;
+  volatile u_char IIR;
+  volatile u_char skip3;
+  volatile u_char LCR;
+  volatile u_char skip4;
+  volatile u_char MCR;
+  volatile u_char skip5;
+  volatile u_char LSR;
+  volatile u_char skip6;
+  volatile u_char MSR;
+  volatile u_char skip7;
+  volatile u_char SCR;
+};
+
+#define THR RBR
+#define FCR IIR
+#define DLL RBR
+#define DLM IER
+#define AFR IIR
+
+/*
+ * Bit-defines for the various registers.
+ */
+
+
+/* IER */
+
+#define ERDAI         (1<<0)
+#define ETHREI        (1<<1)
+#define ELSI          (1<<2)
+#define EMSI          (1<<3)
+
+/* IIR - Interrupt Ident. Register */
+
+#define IRQ_PEND      (1<<0) /* NOTE: IRQ_PEND=0 implies irq pending */
+#define IRQ_ID1       (1<<1)
+#define IRQ_ID2       (1<<2)
+#define IRQ_ID3       (1<<3)
+#define FIFO_ENA0     (1<<6) /* Both these are set when FCR(1<<0)=1 */
+#define FIFO_ENA1     (1<<7)
+
+#define IRQ_RLS  (IRQ_ID1 | IRQ_ID2)
+#define IRQ_RDA  (IRQ_ID2)
+#define IRQ_CTI  (IRQ_ID2 | IRQ_ID3)
+#define IRQ_THRE (IRQ_ID1)
+#define IRQ_MS   0
+
+/* FCR - FIFO Control Register */
+
+#define FIFO_ENA      (1<<0)
+#define RCVR_FIFO_RES (1<<1)
+#define XMIT_FIFO_RES (1<<2)
+#define DMA_MODE_SEL  (1<<3)
+#define RCVR_TRIG_LSB (1<<6)
+#define RCVR_TRIG_MSB (1<<7)
+
+#define FIFO_TRIG_1   0x00
+#define FIFO_TRIG_4   RCVR_TRIG_LSB
+#define FIFO_TRIG_8   RCVR_TRIG_MSB
+#define FIFO_TRIG_14  RCVR_TRIG_LSB|RCVR_TRIG_MSB
+
+/* LCR - Line Control Register */
+
+#define WLS0          (1<<0)
+#define WLS1          (1<<1)
+#define STB           (1<<2)
+#define PEN           (1<<3)
+#define EPS           (1<<4)
+#define STICK_PARITY  (1<<5)
+#define SET_BREAK     (1<<6)
+#define DLAB          (1<<7)
+
+#define data_5bit      0x00
+#define data_6bit      0x01
+#define data_7bit      0x02
+#define data_8bit      0x03
+
+
+/* MCR - Modem Control Register */
+
+#define DTR           (1<<0)
+#define RTS           (1<<1)
+#define OUT1          (1<<2)
+#define OUT2          (1<<3)
+#define LOOP          (1<<4)
+
+/* LSR - Line Status Register */
+
+#define DR            (1<<0)
+#define OE            (1<<1)
+#define PE            (1<<2)
+#define FE            (1<<3)
+#define BI            (1<<4)
+#define THRE          (1<<5)
+#define TEMT          (1<<6)
+#define RCVR_FIFO_ERR (1<<7)
+
+/* MSR - Modem Status Register */
+
+#define DCTS          (1<<0)
+#define DDSR          (1<<1)
+#define TERI          (1<<2)
+#define DDCD          (1<<3)
+#define CTS           (1<<4)
+#define DSR           (1<<5)
+#define RING_I        (1<<6)
+#define DCD           (1<<7)
+
+/* AFR - Alternate Function Register */
+
+#define CONCUR_WRITE  (1<<0)
+#define BAUDOUT       (1<<1)
+#define RXRDY         (1<<2)
+
+
+
+/* Parallel stuff */
+
+/*
+ * Unfortunately National Semiconductors did not supply the
+ * specifications for the parallel port in the chip :-(
+ * TI succed though, so here they are :-)
+ */
+struct IOEXT_par {
+  volatile u_char skip0;
+  volatile u_char DATA;
+  volatile u_char skip1;
+  volatile u_char STATUS;
+  volatile u_char skip2;
+  volatile u_char CTRL;
+};
+
+/*
+ * STATUS (the status-register - read-only)
+ * NOTE: Several of them are active low (!)
+ */
+#define PRINT         (1<<2) /* !PRINT         */
+#define ERR           (1<<3) /* !ERR           */
+#define SLCT          (1<<4) /* SLCT           */
+#define PAE           (1<<5) /* Paper empty    */
+#define ACK           (1<<6) /* !ACK           */
+#define BSY           (1<<7) /* !BSY           */
+
+/*
+ * CTRL
+ */
+#define STRB          (1<<0) /* Printer strobe */
+#define AFD           (1<<1)
+#define INIT          (1<<2)
+#define SLIN          (1<<3)
+#define INT2EN        (1<<4)
+#define DIR           (1<<5)
+
+#endif
diff -urN pc-2.0.21/drivers/char/Makefile linux-2.0.21/drivers/char/Makefile
--- pc-2.0.21/drivers/char/Makefile	Thu Aug 22 17:45:56 1996
+++ linux-2.0.21/drivers/char/Makefile	Thu Sep 26 17:10:41 1996
@@ -20,9 +20,126 @@
 
 L_TARGET := char.a
 M_OBJS   :=
-L_OBJS   := tty_io.o n_tty.o console.o \
-	tty_ioctl.o pty.o vt.o mem.o vc_screen.o random.o \
-	consolemap.o selection.o
+L_OBJS   := tty_io.o n_tty.o tty_ioctl.o pty.o vt.o mem.o vc_screen.o \
+            consolemap.o selection.o random.o
+
+ifeq "$(ARCH)" "i386"
+L_OBJS += console.o keyboard.o defkeymap.o
+endif
+
+ifeq "$(ARCH)" "alpha"
+L_OBJS += console.o keyboard.o defkeymap.o
+endif
+
+ifeq "$(ARCH)" "m68k"
+L_OBJS += fbmem.o keyboard.o defkeymap.o
+endif
+
+ifeq ($(CONFIG_AMIGA_GSP),y)
+M = y
+L_OBJS += tiga.o
+else
+  ifeq ($(CONFIG_AMIGA_GSP),m)
+    M_OBJS += tiga.o
+    MM = m
+  endif
+endif
+
+ifeq ($(CONFIG_AMIGAMOUSE),y)
+M = y
+L_OBJS += amigamouse.o
+else
+  ifeq ($(CONFIG_AMIGAMOUSE),m)
+    M_OBJS += amigamouse.o
+    MM = m
+  endif
+endif
+
+ifeq ($(CONFIG_ATARIMOUSE),y)
+M = y
+L_OBJS += atarimouse.o
+else
+  ifeq ($(CONFIG_ATARIMOUSE),m)
+    M_OBJS += atarimouse.o
+    MM = m
+  endif
+endif
+
+ifeq ($(CONFIG_ATARI_MFPSER),y)
+L_OBJS += atari_MFPser.o
+S = y
+else
+  ifeq ($(CONFIG_ATARI_MFPSER),m)
+  M_OBJS += atari_MFPser.o
+  SM = y
+  endif
+endif
+
+ifeq ($(CONFIG_ATARI_SCC),y)
+L_OBJS += atari_SCC.o
+S = y
+else
+  ifeq ($(CONFIG_ATARI_SCC),m)
+  M_OBJS += atari_SCC.o
+  SM = y
+  endif
+endif
+
+ifeq ($(CONFIG_ATARI_MIDI),y)
+L_OBJS += atari_MIDI.o
+S = y
+else
+  ifeq ($(CONFIG_ATARI_MIDI),m)
+  M_OBJS += atari_MIDI.o
+  SM = y
+  endif
+endif
+
+ifeq ($(CONFIG_AMIGA_BUILTIN_SERIAL),y)
+L_OBJS += amiga_ser.o
+S = y
+else
+  ifeq ($(CONFIG_AMIGA_BUILTIN_SERIAL),m)
+  M_OBJS += amiga_ser.o
+  SM = y
+  endif
+endif
+
+ifeq ($(CONFIG_GVPIOEXT),y)
+L_OBJS += ser_ioext.o
+S = y
+#  else
+#  ifeq ($(CONFIG_GVPIOEXT),m)
+#  M_OBJS += ser_ioext.o
+#  SM = y
+#  endif
+endif
+
+ifeq ($(CONFIG_MULTIFACE_III_TTY),y)
+L_OBJS += ser_mfc.o
+S = y
+else
+  ifeq ($(CONFIG_MULTIFACE_III_TTY),m)
+  M_OBJS += ser_mfc.o
+  SM = y
+  endif
+endif
+
+ifeq ($(ARCH),m68k)
+
+ifdef CONFIG_USERIAL
+S = y
+endif
+
+ifdef S
+LX_OBJS += m68kserial.o
+else
+  ifdef SM
+  MX_OBJS += m68kserial.o
+  endif
+endif
+
+else
 
 ifeq ($(CONFIG_SERIAL),y)
   ifndef CONFIG_SUN_SERIAL
@@ -34,8 +151,6 @@
   endif
 endif
 
-ifndef CONFIG_SUN_KEYBOARD
-L_OBJS += keyboard.o defkeymap.o
 endif
 
 ifeq ($(CONFIG_DIGI),y)
@@ -98,11 +213,39 @@
   endif
 endif
 
-ifeq ($(CONFIG_PRINTER),y)
-L_OBJS += lp.o
-else
-  ifeq ($(CONFIG_PRINTER),m)
-  M_OBJS += lp.o
+ifeq ($(ARCH),m68k)
+  ifeq ($(CONFIG_PRINTER),y)
+  L = y
+  L_OBJS += lp_intern.o
+  else
+    ifeq ($(CONFIG_PRINTER),m)
+    M_OBJS += lp_intern.o
+    LM = m
+    endif
+  endif
+  ifeq ($(CONFIG_MULTIFACE_III_LP),y)
+  L = y
+  L_OBJS += lp_mfc.o
+  else 
+    ifeq ($(CONFIG_MULTIFACE_III_LP),m)
+    M_OBJS += lp_mfc.o
+    LM = m
+    endif
+  endif
+  ifdef L
+    LX_OBJS += lp_m68k.o
+  else
+    ifdef LM
+      MX_OBJS += lp_m68k.o
+    endif
+  endif
+else
+  ifeq ($(CONFIG_PRINTER),y)
+  L_OBJS += lp.o
+  else
+    ifeq ($(CONFIG_PRINTER),m)
+    M_OBJS += lp.o
+    endif
   endif
 endif
 
@@ -212,12 +355,14 @@
   endif
 endif  
 
+ifneq "$(ARCH)" "m68k" 
 ifdef CONFIG_TGA_CONSOLE
 L_OBJS += tga.o
 else
  ifndef CONFIG_SUN_CONSOLE
  L_OBJS += vga.o vesa_blank.o
  endif
+endif
 endif
 
 include $(TOPDIR)/Rules.make
diff -urN pc-2.0.21/drivers/char/amiga_ser.c linux-2.0.21/drivers/char/amiga_ser.c
--- pc-2.0.21/drivers/char/amiga_ser.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/amiga_ser.c	Tue Sep  3 23:56:19 1996
@@ -0,0 +1,471 @@
+/*
+ * drivers/char/amiga_ser.c: Amiga built-in serial port driver.
+ *
+ * Copyright 1994 Roman Hodek, 1994 Hamish Macdonald
+ *       Based on the Atari MFP driver by Roman Hodek
+ *
+ * Modifications by Matthias Welwarsky <Matthias.Welwarsky@ppp.th-darmstadt.de>
+ * - fixed reentrancy problem in ser_tx_int()
+ * 
+ * 27/04/96 - Jes Soerensen: Upgraded for Linux-1.3.x.
+ * 02/09/96 - Jes Soerensen: Moved the {request,free}_irq call for
+ *            AMIGA_VERTB interrupts into the init/deinit funtions as
+ *            there is no reason to service the ser_vbl_int when the
+ *            serial port is not in use.
+ *
+ * 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.
+ *
+ */
+
+
+/*
+ * This file implements the driver for the Amiga built-in serial port.
+ */
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/interrupt.h>
+#include <linux/serial.h>
+#include <linux/linkage.h>
+
+#include <asm/setup.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/traps.h>
+#include <asm/irq.h>
+#include <asm/atomic.h>
+
+/* 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 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)
+
+/***************************** Prototypes *****************************/
+
+static void ser_rx_int( int irq, void *data, struct pt_regs *fp);
+static void ser_tx_int( int irq, void *data, struct pt_regs *fp);
+static void ser_vbl_int( int irq, void *data, struct pt_regs *fp);
+static void ser_init( struct async_struct *info );
+static void ser_deinit( struct async_struct *info, int leave_dtr );
+static void ser_enab_tx_int( struct async_struct *info, int enab_flag );
+static int  ser_check_custom_divisor (struct async_struct *info,
+				      int baud_base, int divisor);
+static void ser_change_speed( struct async_struct *info );
+static void ser_throttle( struct async_struct *info, int status );
+static void ser_set_break( struct async_struct *info, int break_flag );
+static void ser_get_serial_info( struct async_struct *info,
+				struct serial_struct *retinfo );
+static unsigned int ser_get_modem_info( struct async_struct *info );
+static int ser_set_modem_info( struct async_struct *info, int new_dtr,
+			      int new_rts );
+static void ser_stop_receive( struct async_struct *info );
+static int ser_trans_empty( struct async_struct *info );
+
+/************************* End of Prototypes **************************/
+
+
+
+/* SERIALSWITCH structure for the Amiga serial port
+ */
+
+static SERIALSWITCH amiga_ser_switch = {
+    ser_init, ser_deinit, ser_enab_tx_int,
+    ser_check_custom_divisor, ser_change_speed,
+    ser_throttle, ser_set_break,
+    ser_get_serial_info, ser_get_modem_info,
+    ser_set_modem_info, NULL, ser_stop_receive, ser_trans_empty, NULL
+};
+
+/* Standard speeds table */
+static int baud_table[17] = {
+    /* B0     */ 0,
+    /* B50    */ 50,
+    /* B75    */ 75,
+    /* B110   */ 110,
+    /* B134   */ 134,
+    /* B150   */ 150,
+    /* B200   */ 200,
+    /* B300   */ 300,
+    /* B600   */ 600,
+    /* B1200  */ 1200,
+    /* B1800  */ 1800,
+    /* B2400  */ 2400,
+    /* B4800  */ 4800,
+    /* B9600  */ 9600,
+    /* B19200 */ 19200,
+    /* B38400 */ 38400,
+    /* B57600 */ 57600
+};
+	
+static __inline__ void ser_DTRoff(void)
+{
+    ciab.pra |= SER_DTR; /* active low */
+}
+
+static __inline__ void ser_DTRon(void)
+{
+    ciab.pra &= ~SER_DTR; /* active low */
+}
+
+static __inline__ void ser_RTSoff(void)
+{
+    ciab.pra |= SER_RTS; /* active low */
+}
+
+static __inline__ void ser_RTSon(void)
+{
+    ciab.pra &= ~SER_RTS; /* active low */
+}
+
+static int line; /* the serial line assigned by register_serial() */
+/* use this value in isr's to get rid of the data pointer in the future */
+/* This variable holds the current state of the DCD/CTS bits */
+static unsigned char current_ctl_bits;
+
+static __inline__ void check_modem_status(struct async_struct *info)
+{
+	unsigned char bits;
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
+	bits = ciab.pra & (SER_DCD | SER_CTS);
+
+	if (bits ^ current_ctl_bits) {
+		if ((bits ^ current_ctl_bits) & SER_DCD) {
+			rs_dcd_changed(info, !(bits & SER_DCD));
+		}
+
+		if ((bits ^ current_ctl_bits) & SER_CTS)
+			rs_check_cts(info, !(bits & SER_CTS));
+	}
+	current_ctl_bits = bits;
+}
+
+static struct async_struct *amiga_info;
+
+int amiga_serinit( void )
+{	
+	unsigned long flags;
+	struct serial_struct req;
+	struct async_struct *info;
+	
+	if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_SERIAL))
+		return -ENODEV;
+
+	req.line = -1; /* first free ttyS? device */
+	req.type = SER_AMIGA;
+	req.port = (int) &custom.serdatr; /* dummy value */
+	if ((line = register_serial( &req )) < 0) {
+		printk( "Cannot register built-in serial port: no free device\n" );
+		return -EBUSY;
+	}
+    info = &rs_table[line];
+	
+    save_flags (flags);
+    cli();
+
+    /* set ISRs, and then disable the rx interrupts */
+    request_irq(IRQ_AMIGA_TBE, ser_tx_int, 0, "serial TX", info);
+    request_irq(IRQ_AMIGA_RBF, ser_rx_int, 0, "serial RX", info);
+
+    amiga_info = info;
+
+    /* turn off Rx and Tx interrupts */
+    custom.intena = IF_RBF | IF_TBE;
+
+    /* clear any pending interrupt */
+    custom.intreq = IF_RBF | IF_TBE;
+    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 */
+    
+    info->sw   = &amiga_ser_switch;
+
+    return 0;
+}
+
+static void ser_rx_int(int irq, void *data, struct pt_regs *fp)
+{
+      struct async_struct *info = data;
+      int ch;
+
+      ch = custom.serdatr;
+
+      custom.intreq = IF_RBF;
+
+      rs_receive_char(info, ch & 0xff, 
+		      ch & SDR_OVRUN ? TTY_OVERRUN : 0);
+}
+
+static void ser_tx_int( int irq, void *data, struct pt_regs *fp)
+{
+      struct async_struct *info = data;
+      int ch;
+
+      if (custom.serdatr & SDR_TBE) {
+	   if ((ch = rs_get_tx_char(info)) >= 0)
+	        /* write next char */
+	        custom.serdat = ch | 0x100;
+	   if (ch == -1 || rs_no_more_tx( info ))
+	        /* disable tx interrupts */
+	        custom.intena = IF_TBE;
+      }
+}
+
+
+static void ser_vbl_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct async_struct *info = data;
+
+	check_modem_status(info);
+}
+
+
+static void ser_init( struct async_struct *info )
+{	
+	request_irq(IRQ_AMIGA_VERTB, ser_vbl_int, 0,
+		    "serial status", amiga_info);
+
+	/* enable both Rx and Tx interrupts */
+	custom.intena = IF_SETCLR | IF_RBF | IF_TBE;
+
+	/* turn on DTR and RTS */
+	ser_DTRon();
+	ser_RTSon();
+
+	/* remember current state of the DCD and CTS bits */
+	current_ctl_bits = ciab.pra & (SER_DCD | SER_CTS);
+
+	MOD_INC_USE_COUNT;
+}
+
+
+static void ser_enab_tx_int (struct async_struct *info, int enab_flag)
+{
+    if (enab_flag) {
+        unsigned long flags;
+	save_flags(flags);
+	cli();
+	custom.intena = IF_SETCLR | IF_TBE;
+	/* set a pending Tx Interrupt, transmitter should restart now */
+	custom.intreq = IF_SETCLR | IF_TBE;
+	restore_flags(flags);
+    } else {
+	/* disable Tx interrupt and remove any pending interrupts */
+        custom.intena = IF_TBE;
+	custom.intreq = IF_TBE;
+    }
+}
+
+
+static void ser_deinit( struct async_struct *info, int leave_dtr )
+{
+    /* disable Rx and Tx interrupt */
+    custom.intena = IF_RBF | IF_TBE;
+
+    /* wait for last byte to be completely shifted out */
+    while( !(custom.serdatr & SDR_TSRE) )
+	;
+
+    /* drop RTS and DTR if required */
+    ser_RTSoff();
+    if (!leave_dtr)
+	ser_DTRoff();
+
+    free_irq(IRQ_AMIGA_VERTB, amiga_info);
+    MOD_DEC_USE_COUNT;
+}
+
+
+static int ser_check_custom_divisor (struct async_struct *info,
+				     int baud_base, int divisor)
+{
+    /* allow any divisor */
+    return 0;
+}
+
+
+static void ser_change_speed( struct async_struct *info )
+
+{
+    unsigned	cflag, baud, chsize, stopb, parity, aflags;
+    unsigned	div = 0;
+    int realbaud;
+	
+    if (!info->tty || !info->tty->termios) return;
+
+    cflag  = info->tty->termios->c_cflag;
+    baud   = cflag & CBAUD;
+    chsize = cflag & CSIZE;
+    stopb  = cflag & CSTOPB;
+    parity = cflag & (PARENB | PARODD);
+    aflags = info->flags & ASYNC_SPD_MASK;
+
+    if (cflag & CRTSCTS)
+	info->flags |= ASYNC_CTS_FLOW;
+    else
+	info->flags &= ~ASYNC_CTS_FLOW;
+    if (cflag & CLOCAL)
+	info->flags &= ~ASYNC_CHECK_CD;
+    else
+	info->flags |= ASYNC_CHECK_CD;
+
+    if (baud & CBAUDEX) {
+	    baud &= ~CBAUDEX;
+	    if (baud < 1 || baud > 2)
+		    info->tty->termios->c_cflag &= ~CBAUDEX;
+	    else
+		    baud += 15;
+    }
+    if (baud == 15) {
+	    if (aflags == ASYNC_SPD_HI)
+		    baud += 1;
+	    if (aflags == ASYNC_SPD_VHI)
+		    baud += 2;
+	    if (aflags == ASYNC_SPD_CUST)
+		    div = info->custom_divisor;
+    }
+    if (!div){
+	/* Maximum  speed is 57600 :-( */
+	if (baud > 16) baud = 16;
+	realbaud = baud_table[baud];
+	if (realbaud)
+	    div = (amiga_colorclock+realbaud/2)/realbaud-1;
+    }
+
+    if (div) {
+	    /* turn on DTR */
+	    ser_DTRon();
+    } else {
+	    /* speed == 0 -> drop DTR */
+	    ser_DTRoff();
+	    return;
+    }
+
+    /* setup the serial port period register */
+    custom.serper = div;
+}
+
+
+static void ser_throttle( struct async_struct *info, int status )
+{
+    if (status)
+	ser_RTSoff();
+    else
+	ser_RTSon();
+}
+
+
+static void ser_set_break( struct async_struct *info, int break_flag )
+{
+    if (break_flag)
+	custom.adkcon = AC_SETCLR | AC_UARTBRK;
+    else
+	custom.adkcon = AC_UARTBRK;
+}
+
+
+static void ser_get_serial_info( struct async_struct *info,
+				struct serial_struct *retinfo )
+{
+    retinfo->baud_base = amiga_colorclock;
+    retinfo->custom_divisor = info->custom_divisor;
+}
+
+
+static unsigned int ser_get_modem_info( struct async_struct *info )
+{
+    unsigned int minfo = ciab.pra;
+
+    return(
+	   ((minfo & SER_DTR) ? 0 : TIOCM_DTR) |
+	   ((minfo & SER_RTS) ? 0 : TIOCM_RTS) |
+	   ((minfo & SER_DCD) ? 0 : TIOCM_CAR) |
+	   ((minfo & SER_CTS) ? 0 : TIOCM_CTS) |
+	   ((minfo & SER_DSR) ? 0 : TIOCM_DSR) |
+	   /* TICM_RNG */ 0
+	   );
+}
+
+
+static int ser_set_modem_info( struct async_struct *info,
+			      int new_dtr, int new_rts )
+{
+    if (new_dtr == 0)
+	ser_DTRoff();
+    else if (new_dtr == 1)
+	ser_DTRon();
+    
+    if (new_rts == 0)
+	ser_RTSoff();
+    else if (new_rts == 1)
+	ser_RTSon();
+
+    return( 0 );
+}
+
+static void ser_stop_receive( struct async_struct *info )
+{
+	/* disable receive interrupts */
+	custom.intena = IF_RBF;
+	/* clear any pending receive interrupt */
+	custom.intreq = IF_RBF;
+}
+
+static int ser_trans_empty( struct async_struct *info )
+{
+       return (custom.serdatr & SDR_TSRE);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+return amiga_serinit();
+}
+
+void cleanup_module(void)
+{
+unregister_serial(line);
+custom.intena = IF_RBF | IF_TBE; /* forbid interrupts */
+custom.intreq = IF_RBF | IF_TBE; /* clear pending interrupts */
+free_irq(IRQ_AMIGA_TBE, amiga_info);
+free_irq(IRQ_AMIGA_RBF, amiga_info);
+}
+#endif
+
+
+/* ------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * End:
+ * ------------------------------------
+ */
diff -urN pc-2.0.21/drivers/char/amigamouse.c linux-2.0.21/drivers/char/amigamouse.c
--- pc-2.0.21/drivers/char/amigamouse.c	Mon May 20 06:54:27 1996
+++ linux-2.0.21/drivers/char/amigamouse.c	Mon Sep  2 23:55:33 1996
@@ -26,6 +26,10 @@
  *   renamed this file mouse.c => busmouse.c
  *
  * Modified for use in the 1.3 kernels by Jes Sorensen.
+ *
+ * Moved the isr-allocation to the mouse_{open,close} calls, as there
+ *   is no reason to service the mouse in the vertical blank isr if
+ *   the mouse is not in use.             Jes Sorensen
  */
 
 #include <linux/module.h>
@@ -39,13 +43,13 @@
 #include <linux/miscdevice.h>
 #include <linux/random.h>
 
+#include <asm/setup.h>
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <asm/irq.h>
 #include <asm/amigamouse.h>
 #include <asm/amigahw.h>
 #include <asm/amigaints.h>
-#include <asm/bootinfo.h>
 
 #define MSE_INT_ON()	mouseint_allowed = 1
 #define MSE_INT_OFF()	mouseint_allowed = 0
@@ -55,7 +59,7 @@
 
 static int mouseint_allowed;
 
-static void mouse_interrupt(int irq, struct pt_regs *fp, void *dummy)
+static void mouse_interrupt(int irq, void *dummy, struct pt_regs *fp)
 {
 	static int lastx=0, lasty=0;
 	int dx, dy;
@@ -172,6 +176,7 @@
 	fasync_mouse(inode, file, 0);
 	if (--mouse.active)
 	  return;
+	free_irq(IRQ_AMIGA_VERTB, mouse_interrupt);
 	MSE_INT_OFF();
 	MOD_DEC_USE_COUNT;
 }
@@ -183,18 +188,29 @@
 
 static int open_mouse(struct inode * inode, struct file * file)
 {
-  if (!mouse.present)
-    return -EINVAL;
-  if (mouse.active++)
-    return 0;
-  mouse.ready = 0;
-  mouse.dx = 0;
-  mouse.dy = 0;
-  mouse.buttons = 0x87;
-  mouse.active = 1;
-  MOD_INC_USE_COUNT;
-  MSE_INT_ON();
-  return 0;
+	if (!mouse.present)
+		return -EINVAL;
+	if (mouse.active++)
+		return 0;
+	/*
+	 *  use VBL to poll mouse deltas
+	 */
+
+	if(request_irq(IRQ_AMIGA_VERTB, mouse_interrupt, 0,
+	               "Amiga mouse", mouse_interrupt)) {
+		mouse.present = 0;
+		printk(KERN_INFO "Installing Amiga mouse failed.\n");
+		return -EIO;
+	}
+
+	mouse.ready = 0;
+	mouse.dx = 0;
+	mouse.dy = 0;
+	mouse.buttons = 0x87;
+	mouse.active = 1;
+	MOD_INC_USE_COUNT;
+	MSE_INT_ON();
+	return 0;
 }
 
 /*
@@ -306,17 +322,6 @@
 	mouse.dy = 0;
 	mouse.wait = NULL;
 
-	/*
-	 *  use VBL to poll mouse deltas
-	 */
-
-	if(!add_isr(IRQ_AMIGA_VERTB, mouse_interrupt, 0, NULL, "Amiga mouse"))
-	{
-		mouse.present = 0;
-		printk(KERN_INFO "Installing Amiga mouse failed.\n");
-		return -EIO;
-	}
-
 	mouse.present = 1;
 
 	printk(KERN_INFO "Amiga mouse installed.\n");
@@ -325,7 +330,7 @@
 }
 
 #ifdef MODULE
-#include <asm/bootinfo.h>
+#include <asm/setup.h>
 
 int init_module(void)
 {
@@ -334,7 +339,6 @@
 
 void cleanup_module(void)
 {
-  remove_isr(IRQ_AMIGA_VERTB, mouse_interrupt, NULL);
-  misc_deregister(&amiga_mouse);
+	misc_deregister(&amiga_mouse);
 }
 #endif
diff -urN pc-2.0.21/drivers/char/atari_MFPser.c linux-2.0.21/drivers/char/atari_MFPser.c
--- pc-2.0.21/drivers/char/atari_MFPser.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/atari_MFPser.c	Sat Aug 24 14:33:59 1996
@@ -0,0 +1,681 @@
+
+
+/*
+ * drivers/char/atari_MFPser.c: Atari MFP serial ports implementation
+ *
+ * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
+ *
+ * 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.
+ *
+ */
+
+
+/* This file implements the MFP serial ports. These come in two
+ * flavors: with or without control lines (RTS, CTS, DTR, ...). They
+ * are distinguished by having two different types, MFP_CTRL and
+ * MFP_BARE, resp. Most of the low-level functions are the same for
+ * both, but some differ.
+ *
+ * Note that some assumptions are made about where to access the
+ * control lines. If the port type is MFP_CTRL, the input lines (CTS
+ * and DCD) are assumed to be in the MFP GPIP register, bits 1 and 2.
+ * The output lines (DTR and RTS) have to be in the Soundchip Port A,
+ * bits 3 and 4. This is the standard ST/TT assigment. If Atari will
+ * build a serial port in future, that uses other registers, you have
+ * to rewrite this code. But if the port type is MFP_BARE, no such
+ * assumptions are necessary. All registers needed are fixed by the
+ * MFP hardware. The only parameter is the MFP base address. This is
+ * used to implement Serial1 for the TT and the (not connected) MFP
+ * port of the Falcon.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/serial.h>
+
+#include <asm/setup.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/irq.h>
+
+#include "atari_MFPser.h"
+
+
+
+/***************************** Prototypes *****************************/
+
+static void MFPser_init_port( struct async_struct *info, int type,
+			     int tt_flag );
+#ifdef MODULE
+static void MFPser_deinit_port( struct async_struct *info, int tt_flag );
+#endif
+static void MFPser_rx_int (int irq, void *data, struct pt_regs *fp);
+static void MFPser_rxerr_int (int irq, void *data, struct pt_regs *fp);
+static void MFPser_tx_int (int irq, void *data, struct pt_regs *fp);
+static void MFPctrl_dcd_int (int irq, void *data, struct pt_regs *fp);
+static void MFPctrl_cts_int (int irq, void *data, struct pt_regs *fp);
+static void MFPctrl_ri_int (int irq, void *data, struct pt_regs *fp);
+static void MFPser_init( struct async_struct *info );
+static void MFPser_deinit( struct async_struct *info, int leave_dtr );
+static void MFPser_enab_tx_int( struct async_struct *info, int enab_flag );
+static int MFPser_check_custom_divisor (struct async_struct *info,
+					int baud_base, int divisor);
+static void MFPser_change_speed( struct async_struct *info );
+static void MFPctrl_throttle( struct async_struct *info, int status );
+static void MFPbare_throttle( struct async_struct *info, int status );
+static void MFPser_set_break( struct async_struct *info, int break_flag );
+static void MFPser_get_serial_info( struct async_struct *info, struct
+                                    serial_struct *retinfo );
+static unsigned int MFPctrl_get_modem_info( struct async_struct *info );
+static unsigned int MFPbare_get_modem_info( struct async_struct *info );
+static int MFPctrl_set_modem_info( struct async_struct *info, int new_dtr,
+                                   int new_rts );
+static int MFPbare_set_modem_info( struct async_struct *info, int new_dtr,
+                                   int new_rts );
+static void MFPser_stop_receive (struct async_struct *info);
+static int MFPser_trans_empty (struct async_struct *info);
+
+/************************* End of Prototypes **************************/
+
+
+
+/* SERIALSWITCH structures for MFP ports
+ * Most functions are common to MFP ports with or without control lines
+ */
+
+static SERIALSWITCH MFPctrl_switch = {
+	MFPser_init, MFPser_deinit, MFPser_enab_tx_int,
+	MFPser_check_custom_divisor, MFPser_change_speed,
+	MFPctrl_throttle, MFPser_set_break,
+	MFPser_get_serial_info, MFPctrl_get_modem_info,
+	MFPctrl_set_modem_info, NULL, MFPser_stop_receive, MFPser_trans_empty,
+	NULL
+};
+
+
+static SERIALSWITCH MFPbare_switch = {
+	MFPser_init, MFPser_deinit, MFPser_enab_tx_int,
+	MFPser_check_custom_divisor, MFPser_change_speed,
+	MFPbare_throttle, MFPser_set_break,
+	MFPser_get_serial_info, MFPbare_get_modem_info,
+	MFPbare_set_modem_info, NULL, MFPser_stop_receive, MFPser_trans_empty,
+	NULL
+};
+
+/* MFP Timer Modes divided by 2 (this already done in the BAUD_BASE */
+int MFP_timer_modes[] = { 2, 5, 8, 25, 32, 50, 100 };
+
+/* Divisors for standard speeds */
+int MFP_baud_table[15] = {
+	/* B0     */ 0,
+	/* B50    */ 768,
+	/* B75    */ 512,
+	/* B110   */ 350, /* really 109.71 bps */
+	/* B134	  */ 286, /* really 134.27 bps */
+	/* B150	  */ 256,
+	/* B200	  */ 192,
+	/* B300	  */ 128,
+	/* B600	  */ 64,
+	/* B1200  */ 32,
+	/* B1800  */ 21,
+	/* B2400  */ 16,
+	/* B4800  */ 8,
+	/* B9600  */ 4,
+	/* B19200 */ 2
+};
+
+
+#define DEFAULT_STMFP_LINE	0	/* ttyS0 */
+#define DEFAULT_TTMFP_LINE	2	/* ttyS2 */
+
+static int stmfp_line = -1, ttmfp_line = -1;
+
+
+int atari_MFPser_init( void )
+
+{	struct serial_struct req;
+	int nr = 0;
+	extern char m68k_debug_device[];
+
+	if (!MACH_IS_ATARI)
+		return( -ENODEV );
+
+	if (ATARIHW_PRESENT(ST_MFP)) {
+		if (!strcmp( m68k_debug_device, "ser1" ))
+			printk(KERN_NOTICE "ST-MFP serial port used as debug device\n" );
+		else {
+			req.line = DEFAULT_STMFP_LINE;
+			req.type = SER_MFP_CTRL;
+			req.port = (int)&mfp;
+			if ((stmfp_line = register_serial( &req )) >= 0) {
+				MFPser_init_port( &rs_table[stmfp_line], req.type, 0 );
+				++nr;
+			}
+			else
+				printk(KERN_WARNING "Cannot allocate ttyS%d for ST-MFP\n", req.line );
+		}
+	}
+
+	if (ATARIHW_PRESENT(TT_MFP)) {
+		req.line = DEFAULT_TTMFP_LINE;
+		req.type = SER_MFP_BARE;
+		req.port = (int)&tt_mfp;
+		if ((ttmfp_line = register_serial( &req )) >= 0) {
+			MFPser_init_port( &rs_table[ttmfp_line], req.type, 1 );
+			++nr;
+		}
+		else
+			printk(KERN_WARNING "Cannot allocate ttyS%d for TT-MFP\n", req.line );
+	}
+
+	return( nr > 0 ? 0 : -ENODEV );
+}
+
+
+static void MFPser_init_port( struct async_struct *info, int type, int tt_flag)
+{
+	/* set ISRs, but don't enable interrupts yet (done in init());
+	 * all ints are choosen of type FAST, and they're really quite fast.
+	 * Furthermore, we have to account for the fact that these are three ints,
+	 * and one can interrupt another. So better protect them against one
+	 * another...
+	 */
+	request_irq(tt_flag ? IRQ_TT_MFP_SEREMPT : IRQ_MFP_SEREMPT,
+	            MFPser_tx_int, IRQ_TYPE_FAST,
+	            tt_flag ? "TT-MFP TX" : "ST-MFP TX", info);
+	request_irq(tt_flag ? IRQ_TT_MFP_RECFULL : IRQ_MFP_RECFULL,
+	            MFPser_rx_int, IRQ_TYPE_FAST,
+	            tt_flag ? "TT-MFP RX" : "ST-MFP RX", info);
+	request_irq(tt_flag ? IRQ_TT_MFP_RECERR : IRQ_MFP_RECERR,
+	            MFPser_rxerr_int, IRQ_TYPE_FAST,
+	            tt_flag ? "TT-MFP RX error" : "ST-MFP RX error", info);
+	/* Tx_err interrupt unused (it signals only that the Tx shift reg
+	 * is empty)
+	 */
+
+	if (type == SER_MFP_CTRL && !tt_flag) {
+		/* The DCD, CTS and RI ints are slow ints, because I
+		   see no races with the other ints */
+		request_irq(IRQ_MFP_DCD, MFPctrl_dcd_int, IRQ_TYPE_SLOW,
+		            "ST-MFP DCD", info);
+		request_irq(IRQ_MFP_CTS, MFPctrl_cts_int, IRQ_TYPE_SLOW,
+		            "ST-MFP CTS", info);
+		request_irq(IRQ_MFP_RI, MFPctrl_ri_int, IRQ_TYPE_SLOW,
+		            "ST-MFP RI", info);
+		/* clear RTS and DTR */
+		GIACCESS( GI_RTS | GI_DTR );
+	}
+
+	info->sw = (type == SER_MFP_CTRL ? &MFPctrl_switch : &MFPbare_switch);
+
+	currMFP(info)->rcv_stat  = 0;	/* disable Rx */
+	currMFP(info)->trn_stat  = 0;	/* disable Tx */
+}
+
+
+#ifdef MODULE
+static void MFPser_deinit_port( struct async_struct *info, int tt_flag )
+
+{
+	free_irq(tt_flag ? IRQ_TT_MFP_SEREMPT : IRQ_MFP_SEREMPT, info);
+	free_irq(tt_flag ? IRQ_TT_MFP_RECFULL : IRQ_MFP_RECFULL, info);
+	free_irq(tt_flag ? IRQ_TT_MFP_RECERR : IRQ_MFP_RECERR, info);
+	if (info->type == SER_MFP_CTRL && !tt_flag) {
+		free_irq(IRQ_MFP_DCD, info );
+		free_irq(IRQ_MFP_CTS, info );
+		free_irq(IRQ_MFP_RI, info);
+	}
+}
+#endif
+
+
+static void MFPser_rx_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct async_struct *info = data;
+	int		ch, stat, err;
+/*	unsigned long flags; */
+
+	stat = currMFP(info)->rcv_stat;
+	ch   = currMFP(info)->usart_dta;
+	/* Frame Errors don't cause a RxErr IRQ! */
+	err  = (stat & RSR_FRAME_ERR) ? TTY_FRAME : 0;
+
+/*	save_flags(flags);
+	cli(); */
+	rs_receive_char (info, ch, err);
+/*	restore_flags(flags); */
+}
+
+
+static void MFPser_rxerr_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct async_struct *info = data;
+	int		ch, stat, err;
+/*	unsigned long flags; */
+
+	stat = currMFP(info)->rcv_stat;
+	ch   = currMFP(info)->usart_dta; /* most probably junk data */
+
+	if (stat & RSR_PARITY_ERR)
+		err = TTY_PARITY;
+	else if (stat & RSR_OVERRUN_ERR)
+		err = TTY_OVERRUN;
+	else if (stat & RSR_BREAK_DETECT)
+		err = TTY_BREAK;
+	else if (stat & RSR_FRAME_ERR)	/* should not be needed */
+		err = TTY_FRAME;
+	else
+		err = 0;
+
+/*	save_flags(flags);
+	cli(); */
+	rs_receive_char (info, ch, err);
+/*	restore_flags(flags); */
+}
+
+
+static void MFPser_tx_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct async_struct *info = data;
+	int ch;
+
+	if (currMFP(info)->trn_stat & TSR_BUF_EMPTY) {
+
+		if ((ch = rs_get_tx_char( info )) >= 0)
+			currMFP(info)->usart_dta = ch;
+		if (ch == -1 || rs_no_more_tx( info ))
+			/* disable tx interrupts */
+			currMFP(info)->int_en_a &= ~0x04;
+	}
+}
+
+
+static void MFPctrl_dcd_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct async_struct *info = data;
+
+	/* Toggle active edge to get next change of DCD! */
+	currMFP(info)->active_edge ^= GPIP_DCD;
+
+	rs_dcd_changed( info, !(currMFP(info)->par_dt_reg & GPIP_DCD) );
+}
+
+
+static void MFPctrl_cts_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct async_struct *info = data;
+
+	/* Toggle active edge to get next change of CTS! */
+	currMFP(info)->active_edge ^= GPIP_CTS;
+
+	rs_check_cts( info, !(currMFP(info)->par_dt_reg & GPIP_CTS) );
+}
+
+
+static void MFPctrl_ri_int(int irq, void *data, struct pt_regs *fp)
+{
+	struct async_struct *info = data;
+	/* update input line counter */
+	info->icount.rng++;
+	wake_up_interruptible(&info->delta_msr_wait);
+}
+
+
+static void MFPser_init( struct async_struct *info )
+{
+	/* base value for UCR */
+	currMFP(info)->usart_ctr = (UCR_PARITY_OFF | UCR_ASYNC_1 |
+				    UCR_CHSIZE_8 | UCR_PREDIV);
+
+	/* enable Rx and clear any error conditions */
+	currMFP(info)->rcv_stat = RSR_RX_ENAB;
+
+	/* enable Tx */
+	currMFP(info)->trn_stat = TSR_TX_ENAB;
+
+	/* enable Rx, RxErr and Tx interrupts */
+	currMFP(info)->int_en_a |= 0x1c;
+	currMFP(info)->int_mk_a |= 0x1c;
+
+	if (info->type == SER_MFP_CTRL) {
+
+		int status;
+
+		/* set RTS and DTR (low-active!) */
+		GIACCESS( ~(GI_RTS | GI_DTR) );
+
+		/* Set active edge of CTS and DCD signals depending on their
+		 * current state.
+		 * If the line status changes between reading the status and
+		 * enabling the interrupt, this won't work :-( How could it be
+		 * done better??
+		 * ++andreas: do it better by looping until stable
+		 */
+		do {
+		    status = currMFP(info)->par_dt_reg & GPIP_CTS;
+		    if (status)
+			currMFP(info)->active_edge &= ~GPIP_CTS;
+		    else
+			currMFP(info)->active_edge |= GPIP_CTS;
+		} while ((currMFP(info)->par_dt_reg & GPIP_CTS) != status);
+
+		do {
+		    status = currMFP(info)->par_dt_reg & GPIP_DCD;
+		    if (status)
+			currMFP(info)->active_edge &= ~GPIP_DCD;
+		    else
+			currMFP(info)->active_edge |= GPIP_DCD;
+		} while ((currMFP(info)->par_dt_reg & GPIP_DCD) != status);
+
+		/* enable CTS and DCD interrupts */
+		currMFP(info)->int_en_b |= 0x06;
+		currMFP(info)->int_mk_b |= 0x06;
+	}
+	MOD_INC_USE_COUNT;
+}
+
+
+static void MFPser_deinit( struct async_struct *info, int leave_dtr )
+{
+	/* disable Rx, RxErr and Tx interrupts */
+	currMFP(info)->int_en_a &= ~0x1c;
+
+	if (info->type == SER_MFP_CTRL) {
+		/* disable CTS and DCD interrupts */
+		currMFP(info)->int_en_b &= ~0x06;
+	}
+
+	/* disable Rx and Tx */
+	currMFP(info)->rcv_stat = 0;
+	currMFP(info)->trn_stat = 0;
+
+	/* wait for last byte to be completely shifted out */
+	while( !(currMFP(info)->trn_stat & TSR_LAST_BYTE_SENT) )
+		;
+
+	if (info->type == SER_MFP_CTRL) {
+		/* drop RTS and DTR if required */
+		MFPser_RTSoff();
+		if (!leave_dtr)
+			MFPser_DTRoff();
+	}
+
+	/* read Rx status and data to clean up */
+	(void)currMFP(info)->rcv_stat;
+	(void)currMFP(info)->usart_dta;
+	MOD_DEC_USE_COUNT;
+}
+
+
+static void MFPser_enab_tx_int( struct async_struct *info, int enab_flag )
+{
+	if (enab_flag) {
+		unsigned long flags;
+		currMFP(info)->int_en_a |= 0x04;
+		save_flags(flags);
+		cli();
+		MFPser_tx_int (0, info, 0);
+		restore_flags(flags);
+	}
+	else
+		currMFP(info)->int_en_a &= ~0x04;
+}
+
+
+static int MFPser_check_custom_divisor (struct async_struct *info,
+					int baud_base, int divisor)
+{
+	int		i;
+
+	if (baud_base != MFP_BAUD_BASE)
+		return -1;
+
+	/* divisor must be a multiple of 2 or 5 (because of timer modes) */
+	if (divisor == 0 || ((divisor & 1) && (divisor % 5) != 0)) return( -1 );
+
+	/* Determine what timer mode would be selected and look if the
+	 * timer value would be greater than 256
+	 */
+	for( i = sizeof(MFP_timer_modes)/sizeof(*MFP_timer_modes)-1; i >= 0; --i )
+		if (divisor % MFP_timer_modes[i] == 0) break;
+	if (i < 0) return( -1 ); /* no suitable timer mode found */
+
+	return (divisor / MFP_timer_modes[i] > 256);
+}
+
+
+static void MFPser_change_speed( struct async_struct *info )
+{
+	unsigned	cflag, baud, chsize, stopb, parity, aflags;
+	unsigned	div = 0, timer_val;
+	int			timer_mode;
+	unsigned long ipl;
+
+	if (!info->tty || !info->tty->termios) return;
+
+	cflag  = info->tty->termios->c_cflag;
+	baud   = cflag & CBAUD;
+	chsize = cflag & CSIZE;
+	stopb  = cflag & CSTOPB;
+	parity = cflag & (PARENB | PARODD);
+	aflags = info->flags & ASYNC_SPD_MASK;
+
+	if (cflag & CRTSCTS)
+		info->flags |= ASYNC_CTS_FLOW;
+	else
+		info->flags &= ~ASYNC_CTS_FLOW;
+	if (cflag & CLOCAL)
+		info->flags &= ~ASYNC_CHECK_CD;
+	else
+		info->flags |= ASYNC_CHECK_CD;
+
+	if (baud & CBAUDEX) {
+		baud &= ~CBAUDEX;
+		if (baud < 1 || baud > 2)
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			baud += 15;
+	}
+	if (baud == 15) {
+		if (aflags == ASYNC_SPD_HI)
+			baud += 1;
+		if (aflags == ASYNC_SPD_VHI)
+			baud += 2;
+		if (aflags == ASYNC_SPD_CUST)
+			div = info->custom_divisor;
+	}
+	if (!div) {
+		/* Maximum MFP speed is 19200 :-( */
+		if (baud > 14) baud = 14;
+		div = MFP_baud_table[baud];
+	}
+
+	if (div) {
+		/* turn on DTR */
+		MFPser_DTRon ();
+	} else {
+		/* speed == 0 -> drop DTR */
+		MFPser_DTRoff();
+		return;
+	}
+
+	/* compute timer value and timer mode (garuateed to succeed, because
+	 * the divisor was checked before by check_custom_divisor(), if it
+	 * is used-supplied)
+	 */
+	for( timer_mode = sizeof(MFP_timer_modes)/sizeof(*MFP_timer_modes)-1;
+		 timer_mode >= 0; --timer_mode )
+		if (div % MFP_timer_modes[timer_mode] == 0) break;
+	timer_val = div / MFP_timer_modes[timer_mode];
+
+	save_flags (ipl);
+	cli();
+	/* disable Rx and Tx while changing parameters */
+	currMFP(info)->rcv_stat = 0;
+	currMFP(info)->trn_stat = 0;
+
+	/* stop timer D to set new timer value immediatly after re-enabling */
+	currMFP(info)->tim_ct_cd &= ~0x07;
+	currMFP(info)->tim_dt_d = timer_val;
+	currMFP(info)->tim_ct_cd |= (timer_mode+1);
+
+	currMFP(info)->usart_ctr =
+		( (parity & PARENB) ?
+		      ((parity & PARODD) ? UCR_PARITY_ODD : UCR_PARITY_EVEN) :
+		      UCR_PARITY_OFF ) |
+		( chsize == CS5 ? UCR_CHSIZE_5 :
+		  chsize == CS6 ? UCR_CHSIZE_6 :
+		  chsize == CS7 ? UCR_CHSIZE_7 :
+						  UCR_CHSIZE_8 ) |
+		( stopb ? UCR_ASYNC_2 : UCR_ASYNC_1 ) |
+		UCR_PREDIV;
+
+	/* re-enable Rx and Tx */
+	currMFP(info)->rcv_stat = RSR_RX_ENAB;
+	currMFP(info)->trn_stat = TSR_TX_ENAB;
+	restore_flags (ipl);
+}
+
+static void MFPctrl_throttle( struct async_struct *info, int status )
+{
+	if (status)
+		MFPser_RTSoff();
+	else
+		MFPser_RTSon();
+}
+
+
+static void MFPbare_throttle( struct async_struct *info, int status )
+{
+	/* no-op */
+}
+
+
+static void MFPser_set_break( struct async_struct *info, int break_flag )
+{
+	if (break_flag)
+		currMFP(info)->trn_stat |= TSR_SEND_BREAK;
+	else
+		currMFP(info)->trn_stat &= ~TSR_SEND_BREAK;
+}
+
+
+static void MFPser_get_serial_info( struct async_struct *info,
+				   struct serial_struct *retinfo )
+{
+	retinfo->baud_base = MFP_BAUD_BASE;
+	retinfo->custom_divisor = info->custom_divisor;
+}
+
+
+static unsigned int MFPctrl_get_modem_info( struct async_struct *info )
+{
+	unsigned	gpip, gi;
+	unsigned int ri;
+	unsigned long ipl;
+
+	save_flags (ipl);
+	cli();
+	gpip = currMFP(info)->par_dt_reg;
+	gi   = GIACCESS( 0 );
+	restore_flags (ipl);
+
+	/* DSR is not connected on the Atari, assume it to be set;
+	 * RI is tested by the RI bitpos field of info, because the RI is
+	 * signalled at different ports on TT and Falcon
+	 * ++andreas: the signals are inverted!
+	 */
+	/* If there is a SCC but no TT_MFP then RI on the ST_MFP is
+	   used for SCC channel b */
+	if (ATARIHW_PRESENT (SCC) && !ATARIHW_PRESENT (TT_MFP))
+		ri = 0;
+	else if (currMFP(info) == &mfp)
+		ri = gpip & GPIP_RI ? 0 : TIOCM_RNG;
+	else
+		ri = 0;
+	return (((gi   & GI_RTS  ) ? 0 : TIOCM_RTS) |
+		((gi   & GI_DTR  ) ? 0 : TIOCM_DTR) |
+		((gpip & GPIP_DCD) ? 0 : TIOCM_CAR) |
+		((gpip & GPIP_CTS) ? 0 : TIOCM_CTS) |
+		TIOCM_DSR | ri);
+}
+
+
+static unsigned int MFPbare_get_modem_info( struct async_struct *info )
+{
+	return( TIOCM_RTS | TIOCM_DTR | TIOCM_CAR | TIOCM_CTS | TIOCM_DSR );
+}
+
+
+static int MFPctrl_set_modem_info( struct async_struct *info,
+				  int new_dtr, int new_rts )
+{
+	if (new_dtr == 0)
+		MFPser_DTRoff();
+	else if (new_dtr == 1)
+		MFPser_DTRon();
+
+	if (new_rts == 0)
+		MFPser_RTSoff();
+	else if (new_rts == 1)
+		MFPser_RTSon();
+
+	return( 0 );
+}
+
+
+static int MFPbare_set_modem_info( struct async_struct *info,
+				  int new_dtr, int new_rts )
+{
+	/* no-op */
+
+	/* Is it right to return an error or should the attempt to change
+	 * DTR or RTS be silently ignored?
+	 */
+	return( -EINVAL );
+}
+
+static void MFPser_stop_receive (struct async_struct *info)
+{
+	/* disable rx and rxerr interrupt */
+	currMFP (info)->int_en_a &= ~0x18;
+
+	/* disable receiver */
+	currMFP (info)->rcv_stat = 0;
+	/* disable transmitter */
+	currMFP (info)->trn_stat = 0;
+}
+
+static int MFPser_trans_empty (struct async_struct *info)
+{
+	return (currMFP (info)->trn_stat & TSR_LAST_BYTE_SENT) != 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return( atari_MFPser_init() );
+}
+
+void cleanup_module(void)
+{
+	if (stmfp_line >= 0) {
+		MFPser_deinit_port( &rs_table[stmfp_line], 0 );
+		unregister_serial( stmfp_line );
+	}
+	if (ttmfp_line >= 0) {
+		MFPser_deinit_port( &rs_table[ttmfp_line], 1 );
+		unregister_serial( ttmfp_line );
+	}
+}
+#endif
diff -urN pc-2.0.21/drivers/char/atari_MFPser.h linux-2.0.21/drivers/char/atari_MFPser.h
--- pc-2.0.21/drivers/char/atari_MFPser.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/atari_MFPser.h	Sat Aug 24 14:34:00 1996
@@ -0,0 +1,120 @@
+
+/*
+ * atari_MFPser.h: Definitions for MFP serial ports
+ *
+ * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ *
+ * 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.
+ *
+ */
+
+
+#ifndef _ATARI_MFPSER_H
+#define _ATARI_MFPSER_H
+
+
+#define	currMFP(info)	((volatile struct MFP *)(info->port))
+
+/* MFP input frequency, divided by 16 (USART prediv for async modes)
+ * and 2 because of each timer run _toggles_ the output, resulting in
+ * half the frequency, and 2 as greatest common divisor of all timer
+ * modes.
+ */
+#define	MFP_BAUD_BASE	(2457600/16/2/2)
+
+/* USART Control Register */
+
+#define	UCR_PARITY_MASK		0x06
+#define	UCR_PARITY_OFF		0x00
+#define	UCR_PARITY_ODD		0x04
+#define	UCR_PARITY_EVEN		0x06
+
+#define	UCR_MODE_MASK		0x18
+#define	UCR_SYNC_MODE		0x00
+#define	UCR_ASYNC_1			0x08
+#define	UCR_ASNYC_15		0x10
+#define	UCR_ASYNC_2			0x18
+
+#define	UCR_CHSIZE_MASK		0x60
+#define	UCR_CHSIZE_8		0x00
+#define	UCR_CHSIZE_7		0x20
+#define	UCR_CHSIZE_6		0x40
+#define	UCR_CHSIZE_5		0x60
+
+#define	UCR_PREDIV			0x80
+
+/* Receiver Status Register */
+
+#define	RSR_RX_ENAB			0x01
+#define	RSR_STRIP_SYNC		0x02
+#define	RSR_SYNC_IN_PRGR	0x04	/* sync mode only */
+#define	RSR_CHAR_IN_PRGR	0x04	/* async mode only */
+#define	RSR_SYNC_SEARCH		0x08	/* sync mode only */
+#define	RSR_BREAK_DETECT	0x08	/* async mode only */
+#define	RSR_FRAME_ERR		0x10
+#define	RSR_PARITY_ERR		0x20
+#define	RSR_OVERRUN_ERR		0x40
+#define	RSR_CHAR_AVAILABLE	0x80
+
+/* Transmitter Status Register */
+
+#define	TSR_TX_ENAB			0x01
+
+#define	TSR_SOMODE_MASK		0x06
+#define	TSR_SOMODE_OPEN		0x00
+#define	TSR_SOMODE_LOW		0x02
+#define	TSR_SOMODE_HIGH		0x04
+#define	TSR_SOMODE_LOOP		0x06
+
+#define	TSR_SEND_BREAK		0x08
+#define	TSR_LAST_BYTE_SENT	0x10
+#define	TSR_HALF_DUPLEX		0x20
+#define	TSR_UNDERRUN		0x40
+#define	TSR_BUF_EMPTY		0x80
+
+/* Control signals in the GPIP */
+
+#define	GPIP_DCD			0x02
+#define	GPIP_CTS			0x04
+#define	GPIP_RI				0x40
+
+
+/* Convenience routine to access RTS and DTR in the Soundchip: It sets
+ * the register to (oldvalue & mask) if mask is negative or (oldvalue
+ * | mask) if positive. It returns the old value. I guess the
+ * parameters will be constants most of the time, so gcc can throw
+ * away the if statement if it isn't needed.
+ */
+ 
+static __inline__ unsigned char GIACCESS( int mask )
+
+{	unsigned char	old;
+	
+	sound_ym.rd_data_reg_sel = 14;
+	old = sound_ym.rd_data_reg_sel;
+
+	if (mask) {
+		if (mask < 0)
+			sound_ym.wd_data = old & mask;
+		else
+			sound_ym.wd_data = old | mask;
+	}
+	
+	return( old );
+}
+
+#define	GI_RTS				0x08
+#define	GI_DTR				0x10
+
+#define	MFPser_RTSon()		GIACCESS( ~GI_RTS )
+#define	MFPser_RTSoff()		GIACCESS( GI_RTS )
+#define	MFPser_DTRon()		GIACCESS( ~GI_DTR )
+#define	MFPser_DTRoff()		GIACCESS( GI_DTR )
+
+
+int atari_MFPser_init( void );
+
+
+#endif /* _ATARI_MFPSER_H */
diff -urN pc-2.0.21/drivers/char/atari_MIDI.c linux-2.0.21/drivers/char/atari_MIDI.c
--- pc-2.0.21/drivers/char/atari_MIDI.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/atari_MIDI.c	Sat Aug 24 14:34:00 1996
@@ -0,0 +1,219 @@
+
+
+/*
+ * drivers/char/atari_MIDI.c: Atari MIDI driver as serial port
+ *
+ * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
+ *
+ * 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.
+ *
+ * Modified for midi by Martin Schaller
+ */
+
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/serial.h>
+
+#include <asm/traps.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atarikb.h>
+
+#include "atari_MFPser.h"
+
+struct async_struct *midi_info;
+
+#define DEFAULT_MIDI_LINE	5	/* ttyS5 */
+
+
+static void MIDI_int(void)	/* called from keyboard int handler */
+
+{	int		ch, stat, err;
+
+	stat = acia.mid_ctrl;
+
+	if (stat & (ACIA_RDRF | ACIA_FE | ACIA_OVRN)) {
+		ch = acia.mid_data;
+		err=0;
+		if (stat & ACIA_FE)
+			err = TTY_FRAME;
+		if (stat & ACIA_OVRN)
+			err = TTY_OVERRUN;
+		rs_receive_char (midi_info, ch, err);
+	}
+	if (acia.mid_ctrl & ACIA_TDRE) {
+
+		if ((ch = rs_get_tx_char( midi_info )) >= 0)
+			acia.mid_data = ch;
+		if (ch == -1 || rs_no_more_tx( midi_info ))
+			/* disable tx interrupts */
+			acia.mid_ctrl = ACIA_DIV16 | ACIA_D8N1S | ACIA_RIE;
+	}
+}
+
+static void MIDI_init( struct async_struct *info )
+
+{
+	/* enable rx interrupts */
+	acia.mid_ctrl = ACIA_DIV16 | ACIA_D8N1S | ACIA_RIE;
+	MOD_INC_USE_COUNT;
+}
+
+
+static void MIDI_deinit( struct async_struct *info, int leave_dtr )
+
+{
+	/* disable Rx and Tx interrupts */
+	acia.mid_ctrl = ACIA_DIV16 | ACIA_D8N1S;
+
+	/* read Rx status and data to clean up */
+
+	(void)acia.mid_ctrl;
+	(void)acia.mid_data;
+	MOD_DEC_USE_COUNT;
+}
+
+
+static void MIDI_enab_tx_int( struct async_struct *info, int enab_flag )
+
+{
+	if (enab_flag) {
+		unsigned long flags;
+		int ch;
+
+		save_flags(flags);
+		cli();
+		acia.mid_ctrl = ACIA_DIV16 | ACIA_D8N1S | ACIA_RIE |
+				ACIA_RLTIE;
+		/* restart the transmitter */
+		if ((ch = rs_get_tx_char( midi_info )) >= 0)
+			acia.mid_data = ch;
+		if (ch == -1 || rs_no_more_tx( midi_info ))
+			/* disable tx interrupts */
+			acia.mid_ctrl = ACIA_DIV16 | ACIA_D8N1S | ACIA_RIE;
+		restore_flags(flags);
+	}
+	else
+		acia.mid_ctrl = ACIA_DIV16 | ACIA_D8N1S | ACIA_RIE;
+}
+
+
+static int MIDI_check_custom_divisor( struct async_struct *info,
+				     int baud_base, int divisor )
+
+{
+	return (-1);
+}
+
+static void MIDI_change_speed( struct async_struct *info )
+
+{
+	/* no-op */
+}
+
+
+static void MIDI_throttle( struct async_struct *info, int status )
+
+{
+	/* no-op */
+}
+
+
+static void MIDI_set_break( struct async_struct *info, int break_flag )
+
+{
+	/* no-op */
+}
+
+static unsigned int MIDI_get_modem_info( struct async_struct *info )
+
+{
+	return( TIOCM_RTS | TIOCM_DTR | TIOCM_CAR | TIOCM_CTS | TIOCM_DSR );
+}
+
+
+static void MIDI_get_serial_info( struct async_struct *info,
+								   struct serial_struct *retinfo )
+
+{
+	retinfo->baud_base = 31250;
+	retinfo->custom_divisor = 1;
+}
+
+static int MIDI_set_modem_info( struct async_struct *info,
+				int new_dtr, int new_rts )
+
+{
+	/* no-op */
+
+	/* Is it right to return an error or should the attempt to change
+	 * DTR or RTS be silently ignored?
+	 */
+	return( -EINVAL );
+}
+
+static void MIDI_stop_receive (struct async_struct *info)
+{
+	/* disable receive interrupts */
+	acia.mid_ctrl = ACIA_DIV16 | ACIA_D8N1S;
+}
+
+static int MIDI_trans_empty (struct async_struct *info)
+{
+	return (acia.mid_ctrl & ACIA_TDRE) != 0;
+}
+
+/* SERIALSWITCH structures for MIDI port
+ */
+
+static SERIALSWITCH MIDI_switch = {
+	MIDI_init, MIDI_deinit, MIDI_enab_tx_int,
+	MIDI_check_custom_divisor, MIDI_change_speed,
+	MIDI_throttle, MIDI_set_break,
+	MIDI_get_serial_info, MIDI_get_modem_info,
+	MIDI_set_modem_info, NULL, MIDI_stop_receive, MIDI_trans_empty, NULL
+};
+
+int atari_MIDI_init( void )
+
+{	struct serial_struct req;
+	int line;
+
+	req.line = DEFAULT_MIDI_LINE;
+	req.type = SER_MIDI;
+	req.port = (int) &acia.mid_ctrl;
+	if ((line = register_serial( &req )) < 0) {
+		printk(KERN_WARNING "Cannot allocate ttyS%d for MIDI\n", req.line );
+		return line;
+	}
+
+	midi_info = &rs_table[line];
+	midi_info->sw = &MIDI_switch;
+	acia.mid_ctrl = ACIA_DIV16 | ACIA_D8N1S;
+	atari_MIDI_interrupt_hook = MIDI_int;
+
+	return( 0 );
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return( atari_MIDI_init() );
+}
+
+void cleanup_module(void)
+{
+	atari_MIDI_interrupt_hook = NULL;
+	unregister_serial( midi_info->line );
+}
+#endif
diff -urN pc-2.0.21/drivers/char/atari_MIDI.h linux-2.0.21/drivers/char/atari_MIDI.h
--- pc-2.0.21/drivers/char/atari_MIDI.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/atari_MIDI.h	Sat Aug 24 14:34:01 1996
@@ -0,0 +1,7 @@
+
+#ifndef _ATARI_MIDI_H
+#define _ATARI_MIDI_H
+
+int atari_MIDI_init( void );
+
+#endif /* _ATARI_MIDI_H */
diff -urN pc-2.0.21/drivers/char/atari_SCC.c linux-2.0.21/drivers/char/atari_SCC.c
--- pc-2.0.21/drivers/char/atari_SCC.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/atari_SCC.c	Sat Aug 24 14:34:01 1996
@@ -0,0 +1,1362 @@
+/*
+ * drivers/char/atari_SCC.c: Atari SCC serial ports implementation
+ *
+ * Copyright 1994-95 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
+ *
+ * Some parts were taken from the (incomplete) SCC driver by Lars Brinkhoff
+ * <f93labr@dd.chalmers.se>
+ *
+ * Adapted to 1.2 by Andreas Schwab
+ *
+ * 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.
+ *
+ * Description/Notes:
+ *
+ *  - This driver currently handles the asynchronous modes of the SCC ports
+ *    only. Synchronous operation or packet modes aren't implemented yet.
+ *
+ *  - Since there are many variations how the SCC can be integrated, the
+ *    driver offers the possibility to provide the frequencies attached to the
+ *    various clock inputs via an ioctl (along with an externally calculated
+ *    baud table).
+ *
+ *  - I haven't spent much time for optimizations yet...
+ *
+ *  - Sorry, DMA for channel A in the TT isn't supported yet.
+ *
+ *  - Channel A is accessible via two different devices: ttyS3 and ttyS4. The
+ *    former is the RS232 "Serial2" port, the latter the RS422 "LAN" port.
+ *    Only one of these devices can be open at one time.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/serial.h>
+
+#include <asm/setup.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/irq.h>
+#include <asm/atari_SCCserial.h>
+
+#include "atari_SCC.h"
+
+#define	DEBUG_INT	0x01
+#define	DEBUG_INIT	0x02
+#define	DEBUG_THROTTLE	0x04
+#define	DEBUG_INFO	0x08
+#define	DEBUG_SPEED	0x10
+#define	DEBUG_OPEN	0x20
+#define	DEBUG_OVERRUNS	0x40
+
+#define	DEBUG_ALL	0xffffffff
+#define	DEBUG_NONE	0
+
+#define	DEBUG	( DEBUG_NONE )
+
+#define CHANNEL_A	0
+#define CHANNEL_B	1
+
+
+/* Shadows for all SCC write registers */
+static unsigned char SCC_shadow[2][16];
+
+/* To keep track of STATUS_REG state for detection of Ext/Status int source */
+static unsigned char SCC_last_status_reg[2];
+
+/* This array tells the clocks connected to RTxC or TRxC, resp., (2nd
+ * index) for each channel (1st index).
+ *
+ * This table is initialzed for the TT. If we run on another machine,
+ * the values are changed by the initialization function.
+ */
+
+static unsigned SCC_clocks[2][2] = {
+	  /* RTxC */			/* TRxC */
+	{ SCC_BAUD_BASE_PCLK4,	SCC_BAUD_BASE_NONE },	/* Channel A */
+	{ SCC_BAUD_BASE_TIMC,	SCC_BAUD_BASE_BCLK }	/* Channel B */
+};
+
+/* The SCC's master clock (as variable, in case someone has unusual
+ * hardware)
+ */
+
+static unsigned SCC_PCLK = SCC_BAUD_BASE_PCLK;
+
+
+/* BRG values for the standard speeds and the various clock sources */
+
+typedef struct {
+	unsigned	clksrc;		/* clock source to use or -1 for not possible */
+	unsigned	div;		/* divisor: 1, 2 and 4 correspond to
+					 * direct 1:16, 1:32 and 1:64 modes,
+					 * divisors >= 4 yield a BRG value of
+					 * div/2-2 (in 1:16 mode)
+					 */
+} BAUD_ENTRY;
+
+/* A pointer for each channel to the current baud table */
+static BAUD_ENTRY *SCC_baud_table[2];
+
+/* Baud table format:
+ *
+ * Each entry consists of the clock source (CLK_RTxC, CLK_TRxC or
+ * CLK_PCLK) and a divisor. The following rules apply to the divisor:
+ *
+ *   - CLK_RTxC: 1 or even (1, 2 and 4 are the direct modes, > 4 use
+ *               the BRG)
+ *
+ *   - CLK_TRxC: 1, 2 or 4 (no BRG, only direct modes possible)
+ *
+ *   - CLK_PCLK: >= 4 and even (no direct modes, only BRG)
+ *
+ */
+
+/* This table is used if RTxC = 3.672 MHz. This is the case for TT's
+ * channel A and for both channels on the Mega STE/Falcon. (TRxC is unused)
+ */
+static BAUD_ENTRY bdtab_norm[18] = {
+	/* B0      */ { 0, 0 },
+	/* B50     */ { CLK_RTxC, 4590 },
+	/* B75     */ { CLK_RTxC, 3060 },
+	/* B110    */ { CLK_PCLK, 4576 },
+	/* B134    */ { CLK_PCLK, 3756 },
+	/* B150    */ { CLK_RTxC, 1530 },
+	/* B200    */ { CLK_PCLK, 2516 },
+	/* B300    */ { CLK_PCLK, 1678 },
+	/* B600    */ { CLK_PCLK, 838 },
+	/* B1200   */ { CLK_PCLK, 420 },
+	/* B1800   */ { CLK_PCLK, 280 },
+	/* B2400   */ { CLK_PCLK, 210 },
+	/* B4800   */ { CLK_RTxC, 48 },
+	/* B9600   */ { CLK_RTxC, 24 },
+	/* B19200  */ { CLK_RTxC, 12 },
+	/* B38400  */ { CLK_RTxC, 6 },
+	/* B57600  */ { CLK_RTxC, 4 },
+	/* B115200 */ { CLK_RTxC, 2 }
+};
+
+/* This is a special table for the TT channel B with 307.2 kHz at RTxC
+ * and 2.4576 MHz at TRxC
+ */
+static BAUD_ENTRY bdtab_TTChB[18] = {
+	/* B0      */ { 0, 0 },
+	/* B50     */ { CLK_RTxC, 384 },
+	/* B75     */ { CLK_RTxC, 256 },
+	/* B110    */ { CLK_PCLK, 4576 },
+	/* B134    */ { CLK_PCLK, 3756 },
+	/* B150    */ { CLK_RTxC, 128 },
+	/* B200    */ { CLK_RTxC, 96 },
+	/* B300    */ { CLK_RTxC, 64 },
+	/* B600    */ { CLK_RTxC, 32 },
+	/* B1200   */ { CLK_RTxC, 16 },
+	/* B1800   */ { CLK_PCLK, 280 },
+	/* B2400   */ { CLK_RTxC, 8 },
+	/* B4800   */ { CLK_RTxC, 4 },
+	/* B9600   */ { CLK_RTxC, 2 },
+	/* B19200  */ { CLK_RTxC, 1 },
+	/* B38400  */ { CLK_TRxC, 4 },
+	/* B57600  */ { CLK_TRxC, 2 }, /* 57600 is not possible, use 76800 instead */
+	/* B115200 */ { CLK_TRxC, 1 }  /* 115200 is not possible, use 153600 instead */
+};
+
+/* User settable tables */
+static BAUD_ENTRY bdtab_usr[2][18];
+
+
+/*
+   Here are the values to compute the tables above. For each base
+   clock, the BRG values for the common bps rates are listed. The
+   divisor is (BRG+2)*2. For each clock, the 1:16 and 1:32 are also
+   usable (and the BRG isn't used). 1:64 is the same as BRG with
+   k==0. If more than clock source was possible for a bps rate I've
+   choosen the one with the smallest error.
+
+   For 307.2 kHz == base 19200:
+     50    bps -> 190
+     75    bps -> 126
+     110   bps -> 85 (really 110.34 bps, error 0.31 %)
+     134   bps -> 70 (really 133.33 bps, error 0.49 %)
+     150   bps -> 62
+     200   bps -> 46
+     300   bps -> 30
+     600   bps -> 14
+     1200  bps -> 6
+     1800  bps -> 3 (really 1920 bps, error 6.7 %)
+     2400  bps -> 2
+     4800  bps -> 0
+
+   For 2.4576 MHz == base 153600:
+     50    bps -> 1534
+     75    bps -> 1022
+     110   bps -> 696 (really 110.03 bps, error 0.027 %)
+     134   bps -> 571 (really 134.03 bps, error 0.022 %)
+     150   bps -> 510
+     200   bps -> 382
+     300   bps -> 254
+     600   bps -> 126
+     1200  bps -> 62
+     1800  bps -> 41 (really 1786.1 bps, error 0.77 %)
+     2400  bps -> 30
+     4800  bps -> 14
+     9600  bps -> 6
+     19200 bps -> 2
+     38400 bps -> 0
+
+   For 3.672 MHz == base 229500:
+     50    bps -> 2293
+     75    bps -> 1528
+     110   bps -> 1041
+     134   bps -> 854
+     150   bps -> 763
+     200   bps -> 572
+     300   bps -> 381
+     600   bps -> 189
+     1200  bps -> 94
+     1800  bps -> 62
+     2400  bps -> 46
+     4800  bps -> 22
+     9600  bps -> 10
+     19200 bps -> 4
+     38400 bps -> 1
+     57600 bps -> 0
+
+   For 8.053976 MHz == base 503374:
+	  0    	 bps -> 0
+	  50     bps -> 5032
+	  75     bps -> 3354
+	  110    bps -> 2286
+	  134    bps -> 1876
+	  150	 bps -> 1676
+	  200	 bps -> 1256
+	  300	 bps -> 837
+	  600	 bps -> 417
+	  1200   bps -> 208
+	  1800   bps -> 138
+	  2400   bps -> 103
+	  4800   bps -> 50
+	  9600   bps -> 24
+	  19200  bps -> 11
+	  31500  bps -> 6 (really 31461 bps)
+	  50000  bps -> 3
+	  125000 bps -> 0
+
+*/
+
+
+/* Is channel A switchable between two hardware ports? (Serial2/LAN) */
+
+#define	SCCA_SWITCH_SERIAL2_ONLY	0	/* only connected to Serial2 */
+#define	SCCA_SWITCH_LAN_ONLY		1	/* only connected to LAN */
+#define	SCCA_SWITCH_BOTH		2	/* connected to both, switch by
+						 * IO7 in the PSG */
+
+static int SCC_chan_a_switchable;
+
+/* Is channel A (two ports!) already open? */
+static int SCC_chan_a_open;
+
+/* For which line has channel A been opened? */
+static int SCC_chan_a_line;
+
+/* Are the register addresses for the channels reversed? (B before A). This is
+ * the case for the ST_ESCC. */
+static int ChannelsReversed;
+
+/* This macro sets up the 'info' pointer for the interrupt functions of
+ * channel A. It addresses the following problem: The isrs were registered
+ * with callback_data == &rs_table[3] (= Serial2). But they can also be for
+ * &rs_table[4] (LAN), if this port is the open one. SETUP_INFO() thus
+ * advances the pointer if info->line == 3.
+ */
+
+#define DEFAULT_CHANNEL_B_LINE		1 /* ttyS1 */
+#define DEFAULT_CHANNEL_A232_LINE	3 /* ttyS3 */
+#define DEFAULT_CHANNEL_A422_LINE	4 /* ttyS4 */
+
+static int chb_line = -1, cha232_line = -1, cha422_line = -1;
+
+
+#define	SETUP_INFO(info)					\
+	do {							\
+		if (info->line == cha232_line &&		\
+		    SCC_chan_a_line == cha422_line)		\
+			info = &rs_table[cha422_line];		\
+	} while(0)
+
+
+/***************************** Prototypes *****************************/
+
+static void SCC_init_port( struct async_struct *info, int type, int channel );
+#ifdef MODULE
+static void SCC_deinit_port( struct async_struct *info, int channel );
+#endif
+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);
+static int SCC_check_open( struct async_struct *info, struct tty_struct
+                           *tty, struct file *file );
+static void SCC_init( struct async_struct *info );
+static void SCC_deinit( struct async_struct *info, int leave_dtr );
+static void SCC_enab_tx_int( struct async_struct *info, int enab_flag );
+static int SCC_check_custom_divisor( struct async_struct *info, int baud_base,
+				    int divisor );
+static void SCC_change_speed( struct async_struct *info );
+static int SCC_clocksrc( unsigned baud_base, unsigned channel );
+static void SCC_throttle( struct async_struct *info, int status );
+static void SCC_set_break( struct async_struct *info, int break_flag );
+static void SCC_get_serial_info( struct async_struct *info, struct
+				serial_struct *retinfo );
+static unsigned int SCC_get_modem_info( struct async_struct *info );
+static int SCC_set_modem_info( struct async_struct *info, int new_dtr, int
+			      new_rts );
+static int SCC_ioctl( struct tty_struct *tty, struct file *file, struct
+		     async_struct *info, unsigned int cmd, unsigned long
+		     arg );
+static void SCC_stop_receive (struct async_struct *info);
+static int SCC_trans_empty (struct 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
+};
+
+
+int atari_SCC_init( void )
+{
+	struct serial_struct req;
+	int sccdma = ATARIHW_PRESENT(SCC_DMA), escc = ATARIHW_PRESENT(ST_ESCC);
+	int nr = 0;
+	extern char m68k_debug_device[];
+
+	/* SCC present at all? */
+	if (!MACH_IS_ATARI ||
+	    !(ATARIHW_PRESENT(SCC) || ATARIHW_PRESENT(ST_ESCC)))
+		return( -ENODEV );
+
+	/* Channel A is switchable on the TT, MegaSTE and Medusa (extension), i.e.
+	 * all machines with an SCC except the Falcon. If there's a machine where
+	 * channel A is fixed to a RS-232 Serial2, add code to set to
+	 * SCCA_SWITCH_SERIAL2_ONLY.
+	 */
+	if ((boot_info.bi_atari.mch_cookie >> 16) == ATARI_MCH_FALCON)
+		SCC_chan_a_switchable = SCCA_SWITCH_LAN_ONLY;
+	else if (ATARIHW_PRESENT(TT_MFP) ||
+		 ((boot_info.bi_atari.mch_cookie >> 16) == ATARI_MCH_STE &&
+		  (boot_info.bi_atari.mch_cookie & 0xffff)))
+		SCC_chan_a_switchable = SCCA_SWITCH_BOTH;
+	else
+		SCC_chan_a_switchable = SCCA_SWITCH_SERIAL2_ONLY;
+
+	/* General initialization */
+	ChannelsReversed = escc;
+	SCC_chan_a_open = 0;
+
+	/* Init channel B */
+	if (!strcmp( m68k_debug_device, "ser2" ))
+		printk(KERN_NOTICE "SCC channel B: used as debug device\n" );
+	else {
+		req.line = DEFAULT_CHANNEL_B_LINE;
+		req.type = SER_SCC_NORM;
+		req.port = (int)(escc ? &st_escc.cha_b_ctrl : &scc.cha_b_ctrl);
+		if ((chb_line = register_serial( &req )) >= 0) {
+			SCC_init_port( &rs_table[chb_line], req.type, CHANNEL_B );
+			++nr;
+		}
+		else
+			printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel B\n", req.line );
+	}
+
+	/* Init channel A, RS232 part (Serial2) */
+	if (SCC_chan_a_switchable != SCCA_SWITCH_LAN_ONLY) {
+		req.line = DEFAULT_CHANNEL_A232_LINE;
+		req.type = sccdma ? SER_SCC_DMA : SER_SCC_NORM;
+		req.port = (int)(escc ? &st_escc.cha_a_ctrl : &scc.cha_a_ctrl);
+		if ((cha232_line = register_serial( &req )) >= 0) {
+			SCC_init_port( &rs_table[cha232_line], req.type, CHANNEL_A );
+			++nr;
+		}
+		else
+			printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
+	}
+
+	/* Init channel A, RS422 part (LAN) */
+	if (SCC_chan_a_switchable != SCCA_SWITCH_SERIAL2_ONLY) {
+		req.line = DEFAULT_CHANNEL_A422_LINE;
+		req.type = sccdma ? SER_SCC_DMA : SER_SCC_NORM;
+		req.port = (int)(escc ? &st_escc.cha_a_ctrl : &scc.cha_a_ctrl);
+		if ((cha422_line = register_serial( &req )) >= 0) {
+			SCC_init_port( &rs_table[cha422_line], req.type, CHANNEL_A );
+			++nr;
+		}
+		else
+			printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line );
+	}
+
+	return( nr > 0 ? 0 : -ENODEV );
+}
+
+
+static void SCC_init_port( struct async_struct *info, int type, int channel )
+{
+	static int called = 0, ch_a_inited = 0;
+
+	info->sw = &SCC_switch;
+	/* The SERTYPE (with or without DMA) is ignored for now... */
+
+	/* set ISRs, but don't enable interrupts yet (done in init());
+	 * All interrupts are of type PRIORITIZED, which means they can be
+	 * interrupted by all level 6 ints, but not by another SCC (or other level
+	 * 5) int. I see no races with any MFP int, but I'm not quite sure yet
+	 * whether longer delays in between the two-stage SCC register access can
+	 * break things...
+	 */
+	if (channel == CHANNEL_B || !ch_a_inited) {
+		request_irq(channel ? IRQ_SCCB_TX : IRQ_SCCA_TX,
+		            SCC_tx_int, IRQ_TYPE_PRIO,
+		            channel ? "SCC-B TX" : "SCC-A TX", info);
+		request_irq(channel ? IRQ_SCCB_STAT : IRQ_SCCA_STAT,
+		            SCC_stat_int, IRQ_TYPE_PRIO,
+		            channel ? "SCC-B status" : "SCC-A status", info);
+		request_irq(channel ? IRQ_SCCB_RX : IRQ_SCCA_RX,
+		            SCC_rx_int, IRQ_TYPE_PRIO,
+		            channel ? "SCC-B RX" : "SCC-A RX", info);
+		request_irq(channel ? IRQ_SCCB_SPCOND : IRQ_SCCA_SPCOND,
+		            SCC_spcond_int, IRQ_TYPE_PRIO,
+		            channel ? "SCC-B special cond" : "SCC-A special cond", info);
+		if (channel != 0 && ATARIHW_PRESENT (TT_MFP))
+			request_irq(IRQ_TT_MFP_RI, SCC_ri_int, IRQ_TYPE_SLOW,
+			            "TT-MFP RI (modem 2)", info);
+	}
+
+	/* Hardware initialization */
+
+	if (!called) {
+		/* Before accessing the SCC the first time, do a read to the
+		 * control register to reset the internal pointers
+		 */
+		SCCread( STATUS_REG );
+
+		/* The master reset with additional delay */
+		SCCwrite( MASTER_INT_CTRL, MIC_HARD_RESET );
+		udelay(40);
+
+		/* Set the interrupt vector, 0x60 is standard Atari */
+		SCCwrite( INT_VECTOR_REG, 0x60 );
+
+		/* Interrupt parameters: vector includes status, status low */
+		SCCwrite( MASTER_INT_CTRL, MIC_VEC_INCL_STAT );
+
+		/* to do only once, too: If a TT-MFP is present, initialize its timer
+		 * C that is connected to RTxC of channel B */
+		if (ATARIHW_PRESENT(TT_MFP)) {
+			/* If on a TT, program the TT-MFP's timer C to 307200 kHz
+			 * for RTxC on channel B
+			 */
+			tt_mfp.tim_ct_cd = (tt_mfp.tim_ct_cd & ~0x70) | 0x10; /* 1:4 mode */
+			tt_mfp.tim_dt_c  = 1;
+			/* make sure the timer interrupt is disabled, the timer is used
+			 * only for generating a clock */
+			atari_turnoff_irq( IRQ_TT_MFP_TIMC );
+
+			/* Set the baud tables */
+			SCC_baud_table[CHANNEL_A] = bdtab_norm;
+			SCC_baud_table[CHANNEL_B] = bdtab_TTChB;
+
+			/* The clocks are already initialzed for the TT. */
+		}
+		else {
+			/* Set the baud tables */
+			SCC_baud_table[CHANNEL_A] = bdtab_norm;
+			SCC_baud_table[CHANNEL_B] = bdtab_norm;
+
+			/* Set the clocks; only RTxCB is different compared to the TT */
+			SCC_clocks[CHANNEL_B][CLK_RTxC] = SCC_BAUD_BASE_PCLK4;
+		}
+
+		SCCmod( MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB );
+	}
+
+	/* disable interrupts for this channel */
+	SCCwrite( INT_AND_DMA_REG, 0 );
+
+	called = 1;
+	if (CHANNR(info) == CHANNEL_A) ch_a_inited = 1;
+}
+
+
+#ifdef MODULE
+static void SCC_deinit_port( struct async_struct *info, int channel )
+{
+	free_irq(channel ? IRQ_SCCB_TX : IRQ_SCCA_TX, info );
+	free_irq(channel ? IRQ_SCCB_STAT : IRQ_SCCA_STAT, info );
+	free_irq(channel ? IRQ_SCCB_RX : IRQ_SCCA_RX, info );
+	free_irq(channel ? IRQ_SCCB_SPCOND : IRQ_SCCA_SPCOND, info );
+	if (channel != 0 && ATARIHW_PRESENT (TT_MFP))
+		free_irq(IRQ_TT_MFP_RI, info);
+}
+#endif
+
+
+#if DEBUG & DEBUG_OVERRUNS
+static int SCC_ch_cnt[2] = { 0, 0 }, SCC_ch_ovrrun[2] = { 0, 0 };
+#endif
+
+static void SCC_rx_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct async_struct *info = data;
+	unsigned char	ch;
+
+	SETUP_INFO(info);
+
+	ch = SCCread_NB( RX_DATA_REG );
+#if DEBUG & DEBUG_INT
+	printk( "SCC ch %d rx int: char %02x\n", CHANNR(info), ch );
+#endif
+	rs_receive_char (info, ch, 0);
+#if DEBUG & DEBUG_OVERRUNS
+	{ int channel = CHANNR(info);
+	  if (++SCC_ch_cnt[channel] == 10000) {
+		  printk( "SCC ch. %d: overrun rate %d.%02d\n", channel,
+				  SCC_ch_ovrrun[channel] / 100,
+				  SCC_ch_ovrrun[channel] % 100 );
+		  SCC_ch_cnt[channel] = SCC_ch_ovrrun[channel] = 0;
+	  }
+	}
+#endif
+
+	/* Check if another character is already ready; in that case, the
+	 * spcond_int() function must be used, because this character may have an
+	 * error condition that isn't signalled by the interrupt vector used!
+	 */
+	if (SCCread( INT_PENDING_REG ) &
+	    (CHANNR(info) == CHANNEL_A ? IPR_A_RX : IPR_B_RX)) {
+		SCC_spcond_int (0, info, 0);
+		return;
+	}
+
+#ifndef ATARI_USE_SOFTWARE_EOI
+	SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET );
+#endif
+}
+
+
+static void SCC_spcond_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct async_struct *info = data;
+	unsigned char	stat, ch, err;
+	int		int_pending_mask = CHANNR(info) == CHANNEL_A ?
+			                   IPR_A_RX : IPR_B_RX;
+
+	SETUP_INFO(info);
+
+	do {
+
+		stat = SCCread( SPCOND_STATUS_REG );
+		ch = SCCread_NB(RX_DATA_REG);
+#if DEBUG & DEBUG_INT
+		printk( "SCC ch %d spcond int: char %02x stat %02x\n",
+				CHANNR(info), ch, stat );
+#endif
+
+		if (stat & SCSR_RX_OVERRUN)
+			err = TTY_OVERRUN;
+		else if (stat & SCSR_PARITY_ERR)
+			err = TTY_PARITY;
+		else if (stat & SCSR_CRC_FRAME_ERR)
+			err = TTY_FRAME;
+		else
+			err = 0;
+
+		rs_receive_char (info, ch, err);
+
+		/* Overrun and parity errors have to be cleared
+		   manually, else the condition persists for the next
+		   chars */
+		if (stat & (SCSR_RX_OVERRUN | SCSR_PARITY_ERR))
+			SCCwrite(COMMAND_REG, CR_ERROR_RESET);
+
+#if DEBUG & DEBUG_OVERRUNS
+		{ int channel = CHANNR(info);
+		  if (err == TTY_OVERRUN) SCC_ch_ovrrun[channel]++;
+		  if (++SCC_ch_cnt[channel] == 10000) {
+			  printk( "SCC ch. %d: overrun rate %d.%02d %%\n", channel,
+					  SCC_ch_ovrrun[channel] / 100,
+					  SCC_ch_ovrrun[channel] % 100 );
+			  SCC_ch_cnt[channel] = SCC_ch_ovrrun[channel] = 0;
+		  }
+	    }
+#endif
+	} while( SCCread( INT_PENDING_REG ) & int_pending_mask );
+
+#ifndef ATARI_USE_SOFTWARE_EOI
+	SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET );
+#endif
+}
+
+static void SCC_ri_int(int irq, void *data, struct pt_regs *fp)
+{
+	struct async_struct *info = data;
+	/* update input line counter */
+	info->icount.rng++;
+	wake_up_interruptible(&info->delta_msr_wait);
+}
+
+static void SCC_tx_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct async_struct *info = data;
+	int ch;
+
+	SETUP_INFO(info);
+
+	while( (SCCread_NB( STATUS_REG ) & SR_TX_BUF_EMPTY) &&
+		   (ch = rs_get_tx_char( info )) >= 0 ) {
+		SCCwrite( TX_DATA_REG, ch );
+#if DEBUG & DEBUG_INT
+		printk( "SCC ch. %d tx int: sent char %02x\n", CHANNR(info), ch );
+#endif
+	}
+	if (rs_no_more_tx( info )) {
+		SCCwrite( COMMAND_REG, CR_TX_PENDING_RESET );
+		/* disable tx interrupts */
+		SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
+#if DEBUG & DEBUG_INT
+		printk ("SCC ch %d tx int: no more chars\n",
+				CHANNR (info));
+#endif
+	}
+
+#ifndef ATARI_USE_SOFTWARE_EOI
+	SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET );
+#endif
+}
+
+
+static void SCC_stat_int( int irq, void *data, struct pt_regs *fp)
+{
+	struct async_struct *info = data;
+	unsigned channel = CHANNR(info);
+	unsigned char	last_sr, sr, changed;
+
+	SETUP_INFO(info);
+
+	last_sr = SCC_last_status_reg[channel];
+	sr = SCC_last_status_reg[channel] = SCCread_NB( STATUS_REG );
+	changed = last_sr ^ sr;
+#if DEBUG & DEBUG_INT
+	printk( "SCC ch %d stat int: sr=%02x last_sr=%02x\n",
+			CHANNR(info), sr, last_sr );
+#endif
+
+	if (changed & SR_DCD)
+		rs_dcd_changed( info, sr & SR_DCD );
+
+	if (changed & SR_CTS) {
+#if DEBUG & DEBUG_THROTTLE
+		printk( "SCC ch. %d: now CTS=%d\n", CHANNR(info), !!(sr & SR_CTS) );
+#endif
+		rs_check_cts( info, sr & SR_CTS );
+	}
+
+	if (changed & SR_SYNC_ABORT) { /* Data Set Ready */
+		/* update input line counter */
+		info->icount.dsr++;
+		wake_up_interruptible(&info->delta_msr_wait);
+	}
+
+	SCCwrite( COMMAND_REG, CR_EXTSTAT_RESET );
+#ifndef ATARI_USE_SOFTWARE_EOI
+	SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET );
+#endif
+}
+
+
+static int SCC_check_open( struct async_struct *info, struct tty_struct *tty,
+			  struct file *file )
+{
+	unsigned long flags;
+	unsigned char tmp;
+
+	/* If channel A is opened, check if one of the compounded ports (ttyS3 and
+	 * ttyS4) is already open, else activate the appropriate port hardware.
+	 */
+
+#if DEBUG & DEBUG_OPEN
+	printk( "SCC: about to open channel %d as line %d\n",
+			CHANNR(info), info->line );
+#endif
+
+	if (CHANNR(info) == CHANNEL_A) {
+
+		if (SCC_chan_a_open) {
+			if (SCC_chan_a_line != info->line) {
+#if DEBUG & DEBUG_OPEN
+				printk("SCC: channel 0 was already open\n");
+#endif
+				return -EBUSY;
+			}
+			else
+				return 0;
+		}
+
+		if ((info->line == cha232_line &&
+		     SCC_chan_a_switchable == SCCA_SWITCH_LAN_ONLY) ||
+		    (info->line == cha422_line &&
+		     SCC_chan_a_switchable == SCCA_SWITCH_SERIAL2_ONLY))
+			return( -ENODEV );
+
+		SCC_chan_a_open = 1;
+		SCC_chan_a_line = info->line;
+
+		if (SCC_chan_a_switchable == SCCA_SWITCH_BOTH) {
+			save_flags(flags);
+			cli();
+			sound_ym.rd_data_reg_sel = 14;
+			tmp = sound_ym.rd_data_reg_sel;
+			sound_ym.wd_data = (info->line == cha232_line
+					    ? tmp | 0x80
+					    : tmp & 0x7f);
+#if DEBUG & DEBUG_OPEN
+			printk( "SCC: set PSG IO7 to %02x (was %02x)\n",
+			       (info->line & 1) ? (tmp | 0x80) : (tmp & 0x7f),
+			       tmp );
+#endif
+			restore_flags(flags);
+		}
+
+	}
+	return( 0 );
+}
+
+
+static void SCC_init( struct async_struct *info )
+{
+	int i, channel = CHANNR(info);
+	unsigned long	flags;
+	static const struct {
+		unsigned reg, val;
+	} init_tab[] = {
+		/* no parity, 1 stop bit, async, 1:16 */
+		{ AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x64 },
+		/* parity error is special cond, ints disabled, no DMA */
+		{ INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },
+		/* Rx 8 bits/char, no auto enable, Rx off */
+		{ RX_CTRL_REG, RCR_CHSIZE_8 },
+		/* DTR off, Tx 8 bits/char, RTS off, Tx off */
+		{ TX_CTRL_REG, TCR_CHSIZE_8 },
+		/* special features off */
+		{ AUX2_CTRL_REG, 0 },
+		/* RTxC is XTAL, TRxC is input, both clocks = RTxC */
+		{ CLK_CTRL_REG, CCR_TRxCOUT_XTAL | CCR_TXCLK_RTxC | CCR_RXCLK_RTxC },
+		{ DPLL_CTRL_REG, 0 },
+		/* Start Rx */
+		{ RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
+		/* Start Tx */
+		{ TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
+		/* Ext/Stat ints: CTS, DCD, SYNC (DSR) */
+		{ INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT },
+		/* Reset Ext/Stat ints */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* ...again */
+		{ COMMAND_REG, CR_EXTSTAT_RESET },
+		/* Rx int always, TX int off, Ext/Stat int on */
+		{ INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB |
+		  IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL }
+	};
+
+	save_flags(flags);
+	cli();
+
+	SCCmod( MASTER_INT_CTRL, 0x3f,
+	       channel == 0 ? MIC_CH_A_RESET : MIC_CH_B_RESET );
+	udelay(40); /* extra delay after a reset */
+
+	for( i = 0; i < sizeof(init_tab)/sizeof(*init_tab); ++i )
+		SCCwrite( init_tab[i].reg, init_tab[i].val );
+
+	/* remember status register for detection of DCD and CTS changes */
+	SCC_last_status_reg[channel] = SCCread( STATUS_REG );
+	restore_flags(flags);
+#if DEBUG & DEBUG_INIT
+	printk( "SCC channel %d inited\n", CHANNR(info) );
+#endif
+#if DEBUG & DEBUG_OPEN
+	printk( "SCC channel %d opened\n", CHANNR(info) );
+#endif
+	MOD_INC_USE_COUNT;
+}
+
+
+static void SCC_deinit( struct async_struct *info, int leave_dtr )
+{
+	unsigned long	flags, timeout;
+
+#if DEBUG & DEBUG_INIT
+	printk( "SCC channel %d about to be deinited\n", CHANNR(info) );
+#endif
+	save_flags(flags);
+	cli();
+
+	/* disable interrupts */
+	SCCmod( INT_AND_DMA_REG, ~(IDR_EXTSTAT_INT_ENAB | IDR_TX_INT_ENAB |
+							   IDR_RX_INT_SPCOND), 0 );
+
+	/* disable Rx */
+	SCCmod( RX_CTRL_REG, ~RCR_RX_ENAB, 0 );
+
+	/* disable Transmitter */
+	SCCmod( TX_CTRL_REG, ~TCR_TX_ENAB, 0 );
+
+	/* wait until character is completely sent */
+	timeout = jiffies + 50;
+	restore_flags(flags);
+	while( !(SCCread( SPCOND_STATUS_REG ) & SCSR_ALL_SENT) ) {
+		if (jiffies > timeout) break;
+	}
+	save_flags(flags);
+	cli();
+
+	/* drop RTS and maybe DTR */
+	SCCmod( TX_CTRL_REG, ~(TCR_RTS | (leave_dtr ? 0 : TCR_DTR)), 0 );
+
+	restore_flags(flags);
+#if DEBUG & DEBUG_INIT
+	printk( "SCC channel %d deinited\n", CHANNR(info) );
+#endif
+
+	if (CHANNR(info) == CHANNEL_A)
+		SCC_chan_a_open = 0;
+#if DEBUG & DEBUG_OPEN
+	printk( "SCC channel %d closed, chanAlock now = %d\n",
+			CHANNR(info), SCC_chan_a_open );
+#endif
+	MOD_DEC_USE_COUNT;
+}
+
+
+static void SCC_enab_tx_int( struct async_struct *info, int enab_flag )
+{
+	unsigned long	flags;
+	unsigned char	iadr;
+
+	save_flags(flags);
+	cli();
+
+	iadr = SCCread( INT_AND_DMA_REG );
+	if (!!(iadr & IDR_TX_INT_ENAB) != !!enab_flag) {
+		SCCwrite(INT_AND_DMA_REG, iadr ^ IDR_TX_INT_ENAB);
+		if (enab_flag)
+			/* restart the transmitter */
+			SCC_tx_int (0, info, 0);
+#if DEBUG & DEBUG_INT
+		printk("SCC channel %d: tx int %sabled\n", CHANNR(info),
+		       enab_flag ? "en" : "dis");
+#endif
+	}
+
+	restore_flags(flags);
+}
+
+
+static int SCC_check_custom_divisor( struct async_struct *info,
+				    int baud_base, int divisor )
+{
+	int		clksrc;
+
+	clksrc = SCC_clocksrc (baud_base, CHANNR (info));
+	if (clksrc < 0)
+		/* invalid baud base */
+		return( -1 );
+
+	/* check for maximum (BRG values start from 4 with step 2) */
+	if (divisor/2-2 > 65535)
+		return( -1 );
+
+	switch( clksrc ) {
+
+	  case CLK_PCLK:
+		/* The master clock can only be used with the BRG, divisors
+		 * range from 4 and must be a multiple of 2
+		 */
+		return( !(divisor >= 4 && (divisor & 1) == 0) );
+
+	  case CLK_RTxC:
+		/* The RTxC clock can either be used for the direct 1:16, 1:32
+		 * or 1:64 modes (divisors 1, 2 or 4, resp.) or with the BRG
+		 * (divisors from 4 and a multiple of 2)
+		 */
+		return( !(divisor >= 1 && (divisor == 1 || (divisor & 1) == 0)) );
+
+	  case CLK_TRxC:
+		/* The TRxC clock can only be used for direct 1:16, 1:32 or
+		 * 1:64 modes
+		 */
+		return( !(divisor == 1 || divisor == 2 || divisor == 4) );
+
+	}
+	return( -1 );
+}
+
+
+static void SCC_change_speed( struct async_struct *info )
+{
+	unsigned		cflag, baud, chsize, aflags;
+	unsigned		channel, div = 0, clkmode, brgmode, brgval;
+	int clksrc = 0;
+	unsigned long	flags;
+
+	if (!info->tty || !info->tty->termios) return;
+
+	cflag  = info->tty->termios->c_cflag;
+	baud   = cflag & CBAUD;
+	chsize = (cflag & CSIZE) >> 4;
+	aflags = info->flags & ASYNC_SPD_MASK;
+
+	if (cflag & CRTSCTS)
+		info->flags |= ASYNC_CTS_FLOW;
+	else
+		info->flags &= ~ASYNC_CTS_FLOW;
+	if (cflag & CLOCAL)
+		info->flags &= ~ASYNC_CHECK_CD;
+	else
+		info->flags |= ASYNC_CHECK_CD;
+
+	channel = CHANNR(info);
+
+#if DEBUG & DEBUG_SPEED
+	printk( "SCC channel %d: doing new settings:\n", CHANNR(info) );
+	printk( "  baud=%d chsize=%d aflags=%04x base_baud=%d divisor=%d\n",
+			baud, chsize, aflags, info->baud_base, info->custom_divisor );
+#endif
+
+	if (baud == 0 && !aflags) {
+		/* speed == 0 -> drop DTR */
+		save_flags(flags);
+		cli();
+		SCCmod( TX_CTRL_REG, ~TCR_DTR, 0 );
+		restore_flags(flags);
+		return;
+	}
+
+	if (baud & CBAUDEX) {
+		baud &= ~CBAUDEX;
+		if (baud < 1 || baud > 2)
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			baud += 15;
+	}
+	if (baud == 15 && aflags) {
+		switch( aflags) {
+		  case ASYNC_SPD_HI:
+			baud = 16;
+			break;
+		  case ASYNC_SPD_VHI:
+			baud = 17;
+			break;
+		  case ASYNC_SPD_CUST:
+			/* Custom divisor: Compute clock source from the base_baud
+			 * field */
+			if ((clksrc = SCC_clocksrc( info->baud_base, channel )) < 0)
+				/* This shouldn't happen... the baud_base has been checked
+				 * before by check_custom_divisor() */
+				return;
+			div = info->custom_divisor;
+		}
+	}
+
+	if (!div) {
+		if (baud > 17) baud = 17;
+		clksrc = SCC_baud_table[channel][baud].clksrc;
+		div = SCC_baud_table[channel][baud].div;
+	}
+
+	/* compute the SCC's clock source, clock mode, BRG mode and BRG
+	 * value from clksrc and div
+	 */
+	if (div <= 4) {
+		clkmode = (div == 1 ? A1CR_CLKMODE_x16 :
+			   div == 2 ? A1CR_CLKMODE_x32 :
+				      A1CR_CLKMODE_x64);
+		clksrc  = (clksrc == CLK_RTxC
+			   ? CCR_TXCLK_RTxC | CCR_RXCLK_RTxC
+			   : CCR_TXCLK_TRxC | CCR_RXCLK_TRxC);
+		brgmode = 0; /* off */
+		brgval  = 0;
+	}
+	else {
+		brgval  = div/2 - 2;
+		brgmode = (DCR_BRG_ENAB |
+			   (clksrc == CLK_PCLK ? DCR_BRG_USE_PCLK : 0));
+		clkmode = A1CR_CLKMODE_x16;
+		clksrc  = CCR_TXCLK_BRG | CCR_RXCLK_BRG;
+	}
+
+	/* Now we have all parameters and can go to set them: */
+	save_flags(flags);
+	cli();
+#if DEBUG & DEBUG_SPEED
+	printk( "  brgval=%d brgmode=%02x clkmode=%02x clksrc=%02x\n",
+			brgval, brgmode, clkmode, clksrc );
+#endif
+
+	/* receiver's character size */
+	SCCmod( RX_CTRL_REG, ~RCR_CHSIZE_MASK, chsize << 6 );
+#if DEBUG & DEBUG_SPEED
+	printk( "  RX_CTRL_REG <- %02x\n", SCCread( RX_CTRL_REG ) );
+#endif
+
+	/* parity and stop bits (both, Tx and Rx) and clock mode */
+	SCCmod (AUX1_CTRL_REG,
+		~(A1CR_PARITY_MASK | A1CR_MODE_MASK | A1CR_CLKMODE_MASK),
+		((cflag & PARENB
+		  ? (cflag & PARODD ? A1CR_PARITY_ODD : A1CR_PARITY_EVEN)
+		  : A1CR_PARITY_NONE)
+		 | (cflag & CSTOPB ? A1CR_MODE_ASYNC_2 : A1CR_MODE_ASYNC_1)
+		 | clkmode));
+#if DEBUG & DEBUG_SPEED
+	printk( "  AUX1_CTRL_REG <- %02x\n", SCCread( AUX1_CTRL_REG ) );
+#endif
+
+	/* sender's character size */
+	/* Set DTR for valid baud rates! Tnx to jds@kom.auc.dk */
+	SCCmod( TX_CTRL_REG, ~TCR_CHSIZE_MASK, chsize << 5 | TCR_DTR );
+#if DEBUG & DEBUG_SPEED
+	printk( "  TX_CTRL_REG <- %02x\n", SCCread( TX_CTRL_REG ) );
+#endif
+
+	/* clock sources */
+	SCCmod( CLK_CTRL_REG, ~(CCR_TXCLK_MASK | CCR_RXCLK_MASK), clksrc );
+#if DEBUG & DEBUG_SPEED
+	printk( "  CLK_CTRL_REG <- %02x\n", SCCread( CLK_CTRL_REG ) );
+#endif
+
+	/* disable BRG before changing the value */
+	SCCmod( DPLL_CTRL_REG, ~DCR_BRG_ENAB, 0 );
+
+	/* BRG value */
+	SCCwrite( TIMER_LOW_REG, brgval & 0xff );
+	SCCwrite( TIMER_HIGH_REG, (brgval >> 8) & 0xff );
+#if DEBUG & DEBUG_SPEED
+	printk( "  TIMER_LOW_REG <- %02x\n", SCCread( TIMER_LOW_REG ) );
+	printk( "  TIMER_HIGH_REG <- %02x\n", SCCread( TIMER_HIGH_REG ) );
+#endif
+
+	/* BRG enable and clock source */
+	SCCmod( DPLL_CTRL_REG, ~(DCR_BRG_ENAB | DCR_BRG_USE_PCLK), brgmode );
+#if DEBUG & DEBUG_SPEED
+	printk( "  DPLL_CTRL_REG <- %02x\n", SCCread( DPLL_CTRL_REG ) );
+#endif
+
+	restore_flags(flags);
+}
+
+
+static int SCC_clocksrc( unsigned baud_base, unsigned channel )
+{
+	if (baud_base == SCC_PCLK)
+		return( CLK_PCLK );
+	else if (SCC_clocks[channel][CLK_RTxC] != SCC_BAUD_BASE_NONE &&
+		 baud_base == SCC_clocks[channel][CLK_RTxC])
+		return( CLK_RTxC );
+	else if (SCC_clocks[channel][CLK_TRxC] != SCC_BAUD_BASE_NONE &&
+		 baud_base == SCC_clocks[channel][CLK_TRxC])
+		return( CLK_TRxC );
+	else
+		return( -1 );
+}
+
+static void SCC_throttle( struct async_struct *info, int status )
+{
+	unsigned long	flags;
+
+#if DEBUG & DEBUG_THROTTLE
+	printk( "SCC channel %d: throttle %s\n",
+	       CHANNR(info), status ? "full" : "avail" );
+#endif
+	save_flags(flags);
+	cli();
+
+	if (status)
+		SCCmod( TX_CTRL_REG, ~TCR_RTS, 0 );
+	else
+		SCCmod( TX_CTRL_REG, 0xff, TCR_RTS );
+
+#if DEBUG & DEBUG_THROTTLE
+	printk( "  now TX_CTRL_REG = %02x\n", SCCread( TX_CTRL_REG ) );
+#endif
+
+	restore_flags(flags);
+}
+
+
+static void SCC_set_break( struct async_struct *info, int break_flag )
+{
+	unsigned long	flags;
+
+	save_flags(flags);
+	cli();
+
+	if (break_flag) {
+		SCCmod( TX_CTRL_REG, 0xff, TCR_SEND_BREAK );
+	} else {
+		SCCmod( TX_CTRL_REG, ~TCR_SEND_BREAK, 0 );
+	}
+
+	restore_flags(flags);
+}
+
+
+static void SCC_get_serial_info( struct async_struct *info,
+				struct serial_struct *retinfo )
+{
+	retinfo->baud_base = info->baud_base;
+	retinfo->custom_divisor = info->custom_divisor;
+}
+
+
+static unsigned int SCC_get_modem_info( struct async_struct *info )
+{
+	unsigned	sr, tcr, ri, dsr;
+	unsigned long	flags;
+
+	save_flags(flags);
+	cli();
+	sr = SCCread( STATUS_REG );
+	tcr = SCCread( TX_CTRL_REG );
+	restore_flags(flags);
+#if DEBUG & DEBUG_INFO
+	printk( "SCC channel %d: get info, sr=%02x tcr=%02x\n",
+			CHANNR(info), sr, tcr );
+#endif
+	if (CHANNR (info) == 0)
+		ri = 0;
+	else if (ATARIHW_PRESENT (TT_MFP))
+		ri = tt_mfp.par_dt_reg & (1 << 3) ? 0 : TIOCM_RNG;
+	else
+		ri = mfp.par_dt_reg & (1 << 6) ? 0 : TIOCM_RNG;
+
+	if (ATARIHW_PRESENT (ST_ESCC))
+		dsr = st_escc_dsr & (1 << (3 - CHANNR(info))) ? TIOCM_DSR : 0;
+	else
+		dsr = sr & SR_SYNC_ABORT ? TIOCM_DSR : 0;
+
+	return (((tcr & TCR_RTS) ? TIOCM_RTS : 0) |
+		((tcr & TCR_DTR) ? TIOCM_DTR : 0) |
+		((sr & SR_DCD ) ? TIOCM_CAR : 0) |
+		((sr & SR_CTS ) ? TIOCM_CTS : 0) |
+		dsr | ri);
+}
+
+
+static int SCC_set_modem_info( struct async_struct *info,
+			      int new_dtr, int new_rts )
+{
+	unsigned long	flags;
+
+	save_flags(flags);
+	cli();
+
+	if (new_dtr == 0) {
+		SCCmod( TX_CTRL_REG, ~TCR_DTR, 0 );
+	} else if (new_dtr == 1) {
+		SCCmod( TX_CTRL_REG, 0xff, TCR_DTR );
+	}
+
+	if (new_rts == 0) {
+		SCCmod( TX_CTRL_REG, ~TCR_RTS, 0 );
+	} else if (new_rts == 1) {
+		SCCmod( TX_CTRL_REG, 0xff, TCR_RTS );
+	}
+
+#if DEBUG & DEBUG_INFO
+	printk( "SCC channel %d: set info (dtr=%d,rts=%d), now tcr=%02x\n",
+	       CHANNR(info), new_dtr, new_rts, SCCread( TX_CTRL_REG ) );
+#endif
+
+	restore_flags(flags);
+	return( 0 );
+}
+
+static void SCC_stop_receive (struct async_struct *info)
+{
+	/* disable Rx interrupts */
+	SCCmod (INT_AND_DMA_REG, ~IDR_RX_INT_MASK, 0);
+
+	/* disable Rx */
+	SCCmod (RX_CTRL_REG, ~RCR_RX_ENAB, 0);
+}
+
+static int SCC_trans_empty (struct async_struct *info)
+{
+	return (SCCread (SPCOND_STATUS_REG) & SCSR_ALL_SENT) != 0;
+}
+
+static int SCC_ioctl( struct tty_struct *tty, struct file *file,
+		     struct async_struct *info, unsigned int cmd,
+		     unsigned long arg )
+{
+	struct atari_SCCserial *cp = (void *)arg;
+	int error;
+	unsigned channel = CHANNR(info), i, clk, div, rtxc, trxc, pclk;
+
+	switch( cmd ) {
+
+	  case TIOCGATSCC:
+
+		error = verify_area( VERIFY_WRITE, (void *)arg,
+				    sizeof(struct atari_SCCserial) );
+		if (error)
+			return error;
+
+		put_fs_long(SCC_clocks[channel][CLK_RTxC], &cp->RTxC_base);
+		put_fs_long(SCC_clocks[channel][CLK_TRxC], &cp->TRxC_base);
+		put_fs_long(SCC_PCLK, &cp->PCLK_base);
+		memcpy_tofs( cp->baud_table, SCC_baud_table[channel] + 1,
+			    sizeof(cp->baud_table) );
+
+		return( 0 );
+
+	  case TIOCSATSCC:
+
+		if (!suser()) return( -EPERM );
+
+		error = verify_area(VERIFY_READ, (void *)arg,
+				    sizeof(struct atari_SCCserial) );
+		if (error)
+			return error;
+
+		rtxc = get_fs_long( (unsigned long *)&(cp->RTxC_base) );
+		trxc = get_fs_long( (unsigned long *)&(cp->TRxC_base) );
+		pclk = get_fs_long( (unsigned long *)&(cp->PCLK_base) );
+
+		if (pclk == SCC_BAUD_BASE_NONE)
+			/* This is really not possible :-) */
+			return( -EINVAL );
+
+		/* Check the baud table for consistency */
+		for( i = 0; i < sizeof(cp->baud_table)/sizeof(cp->baud_table[0]); ++i ) {
+
+			clk = get_fs_long( (unsigned long *)&(cp->baud_table[i].clksrc) );
+			div = get_fs_long( (unsigned long *)&(cp->baud_table[i].divisor) );
+
+			switch( clk ) {
+			  case CLK_RTxC:
+				if (rtxc == SCC_BAUD_BASE_NONE)
+					return( -EINVAL );
+				if (((div & 1) && div != 1) ||
+				    (div >= 4 && div/2-2 > 65535))
+					return( -EINVAL );
+				break;
+			  case CLK_TRxC:
+				if (trxc == SCC_BAUD_BASE_NONE)
+					return( -EINVAL );
+				if (div != 1 && div != 2 && div != 4)
+					return( -EINVAL );
+				break;
+			  case CLK_PCLK:
+				if (div < 4 || (div & 1) || div/2-2 > 65535)
+					return( -EINVAL );
+				break;
+			  default:
+				/* invalid valid clock source */
+				return( -EINVAL );
+			}
+		}
+
+		/* After all the checks, set the values */
+
+		SCC_clocks[channel][CLK_RTxC] = rtxc;
+		SCC_clocks[channel][CLK_TRxC] = trxc;
+		SCC_PCLK = pclk;
+
+		memcpy_fromfs( bdtab_usr[channel] + 1, cp->baud_table,
+			      sizeof(cp->baud_table) );
+		/* Now use the user supplied baud table */
+		SCC_baud_table[channel] = bdtab_usr[channel];
+
+		return( 0 );
+
+	  case TIOCDATSCC:
+
+		if (!suser()) return( -EPERM );
+
+		if (ATARIHW_PRESENT(TT_MFP)) {
+			SCC_clocks[channel][CLK_RTxC] =
+				(channel == CHANNEL_A) ?
+					SCC_BAUD_BASE_PCLK4 :
+					SCC_BAUD_BASE_TIMC;
+			SCC_clocks[channel][CLK_TRxC] =
+				(channel == CHANNEL_A) ?
+					SCC_BAUD_BASE_NONE :
+					SCC_BAUD_BASE_BCLK;
+		}
+		else {
+			SCC_clocks[channel][CLK_RTxC] = SCC_BAUD_BASE_PCLK4;
+			SCC_clocks[channel][CLK_TRxC] =
+				(channel == CHANNEL_A) ?
+					SCC_BAUD_BASE_NONE :
+					SCC_BAUD_BASE_BCLK;
+		}
+
+		SCC_PCLK = SCC_BAUD_BASE_PCLK;
+		SCC_baud_table[channel] =
+			((ATARIHW_PRESENT(TT_MFP) && channel == 1) ?
+			 bdtab_TTChB : bdtab_norm);
+
+		return( 0 );
+
+	}
+	return( -ENOIOCTLCMD );
+}
+
+
+
+
+#ifdef MODULE
+int init_module(void)
+{
+	return( atari_SCC_init() );
+}
+
+void cleanup_module(void)
+{
+	if (chb_line >= 0) {
+		SCC_deinit_port( &rs_table[chb_line], CHANNEL_B );
+		unregister_serial( chb_line );
+	}
+	if (cha232_line >= 0 || cha422_line >= 0)
+		SCC_deinit_port( &rs_table[cha232_line], CHANNEL_A );
+	if (cha232_line >= 0)
+		unregister_serial( cha232_line );
+	if (cha422_line >= 0)
+		unregister_serial( cha422_line );
+}
+#endif
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  tab-width: 4
+ * End:
+ */
diff -urN pc-2.0.21/drivers/char/atari_SCC.h linux-2.0.21/drivers/char/atari_SCC.h
--- pc-2.0.21/drivers/char/atari_SCC.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/atari_SCC.h	Thu Sep 26 22:40:45 1996
@@ -0,0 +1,574 @@
+/*
+ * atari_SCC.h: Definitions for the Am8530 Serial Communications Controller
+ *
+ * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ *
+ * 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.
+ *
+ */
+
+
+#ifndef _ATARI_SCC_H
+#define _ATARI_SCC_H
+
+#include <asm/atarihw.h>
+
+/***********************************************************************/
+/*                                                                     */
+/*                             Register Names                          */
+/*                                                                     */
+/***********************************************************************/
+
+/* The SCC documentation gives no explicit names to the registers,
+ * they're just called WR0..15 and RR0..15. To make the source code
+ * better readable and make the transparent write reg read access (see
+ * below) possible, I christen them here with self-invented names.
+ * Note that (real) read registers are assigned numbers 16..31. WR7'
+ * has number 33.
+ */
+
+#define	COMMAND_REG			0	/* wo */
+#define	INT_AND_DMA_REG		1	/* wo */
+#define	INT_VECTOR_REG		2	/* rw, common to both channels */
+#define	RX_CTRL_REG			3	/* rw */
+#define	AUX1_CTRL_REG		4	/* rw */
+#define	TX_CTRL_REG			5	/* rw */
+#define	SYNC_ADR_REG		6	/* wo */
+#define	SYNC_CHAR_REG		7	/* wo */
+#define	SDLC_OPTION_REG		33	/* wo */
+#define	TX_DATA_REG			8	/* wo */
+#define	MASTER_INT_CTRL		9	/* wo, common to both channels */
+#define	AUX2_CTRL_REG		10	/* rw */
+#define	CLK_CTRL_REG		11	/* wo */
+#define	TIMER_LOW_REG		12	/* rw */
+#define	TIMER_HIGH_REG		13	/* rw */
+#define	DPLL_CTRL_REG		14	/* wo */
+#define	INT_CTRL_REG		15	/* rw */
+
+#define	STATUS_REG			16	/* ro */
+#define	SPCOND_STATUS_REG	17	/* wo */
+/* RR2 is WR2 for Channel A, Channel B gives vector + current status: */
+#define	CURR_VECTOR_REG		18	/* Ch. B only, Ch. A for rw */
+#define	INT_PENDING_REG		19	/* Channel A only! */
+/* RR4 is WR4, if b6(MR7') == 1 */
+/* RR5 is WR5, if b6(MR7') == 1 */
+#define	FS_FIFO_LOW_REG		22	/* ro */
+#define	FS_FIFO_HIGH_REG	23	/* ro */
+#define	RX_DATA_REG			24	/* ro */
+/* RR9 is WR3, if b6(MR7') == 1 */
+#define	DPLL_STATUS_REG		26	/* ro */
+/* RR11 is WR10, if b6(MR7') == 1 */
+/* RR12 is WR12 */
+/* RR13 is WR13 */
+/* RR14 not present */
+/* RR15 is WR15 */
+
+
+/***********************************************************************/
+/*                                                                     */
+/*                             Register Values                         */
+/*                                                                     */
+/***********************************************************************/
+
+
+/* WR0: COMMAND_REG "CR" */
+
+#define	CR_RX_CRC_RESET			0x40
+#define	CR_TX_CRC_RESET			0x80
+#define	CR_TX_UNDERRUN_RESET	0xc0
+
+#define	CR_EXTSTAT_RESET		0x10
+#define	CR_SEND_ABORT			0x18
+#define	CR_ENAB_INT_NEXT_RX		0x20
+#define	CR_TX_PENDING_RESET		0x28
+#define	CR_ERROR_RESET			0x30
+#define	CR_HIGHEST_IUS_RESET	0x38
+
+
+/* WR1: INT_AND_DMA_REG "IDR" */
+
+#define	IDR_EXTSTAT_INT_ENAB	0x01
+#define	IDR_TX_INT_ENAB			0x02
+#define	IDR_PARERR_AS_SPCOND	0x04
+
+#define	IDR_RX_INT_DISAB		0x00
+#define	IDR_RX_INT_FIRST		0x08
+#define	IDR_RX_INT_ALL			0x10
+#define	IDR_RX_INT_SPCOND		0x18
+#define	IDR_RX_INT_MASK			0x18
+
+#define	IDR_WAITREQ_RX			0x20
+#define	IDR_WAITREQ_IS_REQ		0x40
+#define	IDR_WAITREQ_ENAB		0x80
+
+
+/* WR3: RX_CTRL_REG "RCR" */
+
+#define	RCR_RX_ENAB				0x01
+#define	RCR_DISCARD_SYNC_CHARS	0x02
+#define	RCR_ADDR_SEARCH			0x04
+#define	RCR_CRC_ENAB			0x08
+#define	RCR_SEARCH_MODE			0x10
+#define	RCR_AUTO_ENAB_MODE		0x20
+
+#define	RCR_CHSIZE_MASK			0xc0
+#define	RCR_CHSIZE_5			0x00
+#define	RCR_CHSIZE_6			0x40
+#define	RCR_CHSIZE_7			0x80
+#define	RCR_CHSIZE_8			0xc0
+
+
+/* WR4: AUX1_CTRL_REG "A1CR" */
+
+#define	A1CR_PARITY_MASK		0x03
+#define	A1CR_PARITY_NONE		0x00
+#define	A1CR_PARITY_ODD			0x01
+#define	A1CR_PARITY_EVEN		0x03
+
+#define	A1CR_MODE_MASK			0x0c
+#define	A1CR_MODE_SYNCR			0x00
+#define	A1CR_MODE_ASYNC_1		0x04
+#define	A1CR_MODE_ASYNC_15		0x08
+#define	A1CR_MODE_ASYNC_2		0x0c
+
+#define	A1CR_SYNCR_MODE_MASK	0x30
+#define	A1CR_SYNCR_MONOSYNC		0x00
+#define	A1CR_SYNCR_BISYNC		0x10
+#define	A1CR_SYNCR_SDLC			0x20
+#define	A1CR_SYNCR_EXTCSYNC		0x30
+
+#define	A1CR_CLKMODE_MASK		0xc0
+#define	A1CR_CLKMODE_x1			0x00
+#define	A1CR_CLKMODE_x16		0x40
+#define	A1CR_CLKMODE_x32		0x80
+#define	A1CR_CLKMODE_x64		0xc0
+
+
+/* WR5: TX_CTRL_REG "TCR" */
+
+#define	TCR_TX_CRC_ENAB			0x01
+#define	TCR_RTS					0x02
+#define	TCR_USE_CRC_CCITT		0x00
+#define	TCR_USE_CRC_16			0x04
+#define	TCR_TX_ENAB				0x08
+#define	TCR_SEND_BREAK			0x10
+
+#define	TCR_CHSIZE_MASK			0x60
+#define	TCR_CHSIZE_5			0x00
+#define	TCR_CHSIZE_6			0x20
+#define	TCR_CHSIZE_7			0x40
+#define	TCR_CHSIZE_8			0x60
+
+#define	TCR_DTR					0x80
+
+
+/* WR7': SLDC_OPTION_REG "SOR" */
+
+#define	SOR_AUTO_TX_ENAB		0x01
+#define	SOR_AUTO_EOM_RESET		0x02
+#define	SOR_AUTO_RTS_MODE		0x04
+#define	SOR_NRZI_DISAB_HIGH		0x08
+#define	SOR_ALT_DTRREQ_TIMING	0x10
+#define	SOR_READ_CRC_CHARS		0x20
+#define	SOR_EXTENDED_REG_ACCESS	0x40
+
+
+/* WR9: MASTER_INT_CTRL "MIC" */
+
+#define	MIC_VEC_INCL_STAT		0x01
+#define	MIC_NO_VECTOR			0x02
+#define	MIC_DISAB_LOWER_CHAIN	0x04
+#define	MIC_MASTER_INT_ENAB		0x08
+#define	MIC_STATUS_HIGH			0x10
+#define	MIC_IGN_INTACK			0x20
+
+#define	MIC_NO_RESET			0x00
+#define	MIC_CH_A_RESET			0x40
+#define	MIC_CH_B_RESET			0x80
+#define	MIC_HARD_RESET			0xc0
+
+
+/* WR10: AUX2_CTRL_REG "A2CR" */
+
+#define	A2CR_SYNC_6				0x01
+#define	A2CR_LOOP_MODE			0x02
+#define	A2CR_ABORT_ON_UNDERRUN	0x04
+#define	A2CR_MARK_IDLE			0x08
+#define	A2CR_GO_ACTIVE_ON_POLL	0x10
+
+#define	A2CR_CODING_MASK		0x60
+#define	A2CR_CODING_NRZ			0x00
+#define	A2CR_CODING_NRZI		0x20
+#define	A2CR_CODING_FM1			0x40
+#define	A2CR_CODING_FM0			0x60
+
+#define	A2CR_PRESET_CRC_1		0x80
+
+
+/* WR11: CLK_CTRL_REG "CCR" */
+
+#define	CCR_TRxCOUT_MASK		0x03
+#define	CCR_TRxCOUT_XTAL		0x00
+#define	CCR_TRxCOUT_TXCLK		0x01
+#define	CCR_TRxCOUT_BRG			0x02
+#define	CCR_TRxCOUT_DPLL		0x03
+
+#define	CCR_TRxC_OUTPUT			0x04
+
+#define	CCR_TXCLK_MASK			0x18
+#define	CCR_TXCLK_RTxC			0x00
+#define	CCR_TXCLK_TRxC			0x08
+#define	CCR_TXCLK_BRG			0x10
+#define	CCR_TXCLK_DPLL			0x18
+
+#define	CCR_RXCLK_MASK			0x60
+#define	CCR_RXCLK_RTxC			0x00
+#define	CCR_RXCLK_TRxC			0x20
+#define	CCR_RXCLK_BRG			0x40
+#define	CCR_RXCLK_DPLL			0x60
+
+#define	CCR_RTxC_XTAL			0x80
+
+
+/* WR14: DPLL_CTRL_REG "DCR" */
+
+#define	DCR_BRG_ENAB			0x01
+#define	DCR_BRG_USE_PCLK		0x02
+#define	DCR_DTRREQ_IS_REQ		0x04
+#define	DCR_AUTO_ECHO			0x08
+#define	DCR_LOCAL_LOOPBACK		0x10
+
+#define	DCR_DPLL_EDGE_SEARCH	0x20
+#define	DCR_DPLL_ERR_RESET		0x40
+#define	DCR_DPLL_DISAB			0x60
+#define	DCR_DPLL_CLK_BRG		0x80
+#define	DCR_DPLL_CLK_RTxC		0xa0
+#define	DCR_DPLL_FM				0xc0
+#define	DCR_DPLL_NRZI			0xe0
+
+
+/* WR15: INT_CTRL_REG "ICR" */
+
+#define	ICR_OPTIONREG_SELECT	0x01
+#define	ICR_ENAB_BRG_ZERO_INT	0x02
+#define	ICR_USE_FS_FIFO			0x04
+#define	ICR_ENAB_DCD_INT		0x08
+#define	ICR_ENAB_SYNC_INT		0x10
+#define	ICR_ENAB_CTS_INT		0x20
+#define	ICR_ENAB_UNDERRUN_INT	0x40
+#define	ICR_ENAB_BREAK_INT		0x80
+
+
+/* RR0: STATUS_REG "SR" */
+
+#define	SR_CHAR_AVAIL			0x01
+#define	SR_BRG_ZERO				0x02
+#define	SR_TX_BUF_EMPTY			0x04
+#define	SR_DCD					0x08
+#define	SR_SYNC_ABORT			0x10
+#define	SR_CTS					0x20
+#define	SR_TX_UNDERRUN			0x40
+#define	SR_BREAK				0x80
+
+
+/* RR1: SPCOND_STATUS_REG "SCSR" */
+
+#define	SCSR_ALL_SENT			0x01
+#define	SCSR_RESIDUAL_MASK		0x0e
+#define	SCSR_PARITY_ERR			0x10
+#define	SCSR_RX_OVERRUN			0x20
+#define	SCSR_CRC_FRAME_ERR		0x40
+#define	SCSR_END_OF_FRAME		0x80
+
+
+/* RR3: INT_PENDING_REG "IPR" */
+
+#define	IPR_B_EXTSTAT			0x01
+#define	IPR_B_TX				0x02
+#define	IPR_B_RX				0x04
+#define	IPR_A_EXTSTAT			0x08
+#define	IPR_A_TX				0x10
+#define	IPR_A_RX				0x20
+
+
+/* RR7: FS_FIFO_HIGH_REG "FFHR" */
+
+#define	FFHR_CNT_MASK			0x3f
+#define	FFHR_IS_FROM_FIFO		0x40
+#define	FFHR_FIFO_OVERRUN		0x80
+
+
+/* RR10: DPLL_STATUS_REG "DSR" */
+
+#define	DSR_ON_LOOP				0x02
+#define	DSR_ON_LOOP_SENDING		0x10
+#define	DSR_TWO_CLK_MISSING		0x40
+#define	DSR_ONE_CLK_MISSING		0x80
+
+
+
+/***************************** Prototypes *****************************/
+
+int atari_SCC_init( void );
+
+/************************* End of Prototypes **************************/
+
+/* Compute the channel number from the base address */
+
+#define CHANNR(info)	(((info)->port & 4) != ChannelsReversed)
+
+
+/***********************************************************************/
+/*                                                                     */
+/*                               Constants                             */
+/*                                                                     */
+/***********************************************************************/
+
+
+/***********************************************************************/
+/*                                                                     */
+/*                             Register Access                         */
+/*                                                                     */
+/***********************************************************************/
+
+
+/* The SCC needs 3.5 PCLK cycles recovery time between to register
+ * accesses. PCLK runs with 8 MHz on an Atari, so this delay is 3.5 *
+ * 125 ns = 437.5 ns. Since this is too short for udelay(), it is
+ * implemented by some nop's. I think that a nop needs 4 cycles (but
+ * I'm not sure, correct me please!), that gives 4 nops for a TT (32
+ * MHz) (2 would be sufficient for the Falcon (16 MHz), but looking at
+ * boot_info.bi_atari.model at runtime takes longer than 2 nop's...)
+ * ++andreas: nop needs only 2 cycles, seven of them are needed.
+ */
+
+/* 10/16/95: A tstb mfp.par_dt_reg takes 600ns (sure?) and thus should be
+ * quite right
+ */
+
+static __inline__ void scc_reg_delay( void )
+{
+	MFPDELAY();
+}
+
+/* Another version with only 3 nop's for cases when some other
+ * statement intervenes between the two SCC accesses
+ * ++andreas: 3 nop's added
+ * 10/16/95: use MFPDELAY, too.
+ */
+
+static __inline__ void scc_reg3_delay( void )
+{
+	MFPDELAY();
+}
+
+
+struct PARTIAL_SCC {		/* just one channel */
+	unsigned char	ctrl;
+	unsigned char	dummy;
+	unsigned char	data;
+};
+
+
+extern unsigned char SCC_shadow[2][16];
+extern int ChannelsReversed;
+
+
+/* The following functions should relax the somehow complicated
+ * register access of the SCC. _SCCwrite() stores all written values
+ * (except for WR0 and WR8) in shadow registers for later recall. This
+ * removes the burden of remembering written values as needed. The
+ * extra work of storing the value doesn't count, since a delay is
+ * needed after a SCC access anyway. Additionally, _SCCwrite() manages
+ * writes to WR0 and WR8 differently, because these can be accessed
+ * directly with less overhead. Another special case are WR7 and WR7'.
+ * _SCCwrite automatically checks what of this registers is selected
+ * and changes b0 of WR15 if needed.
+ * 
+ * _SCCread() for standard read registers is straightforward, except
+ * for RR2 (split into two "virtual" registers: one for the value
+ * written to WR2 (from the shadow) and one for the vector including
+ * status from RR2, Ch. B) and RR3. The latter must be read from
+ * Channel A, because it reads as all zeros on Ch. B. RR0 and RR8 can
+ * be accessed directly as before.
+ * 
+ * The two inline function contain complicated switch statements. But
+ * I rely on regno and final_delay being constants, so gcc can reduce
+ * the whole stuff to just some assembler statements.
+ * 
+ * _SCCwrite and _SCCread aren't intended to be used directly under
+ * normal circumstances. The macros SCCread[_ND] and SCCwrite[_ND] are
+ * for that purpose. They assume that a local variable 'info' is
+ * declared and pointing to the port's async_struct entry. The
+ * variants with "_NB" appended should be used if no other SCC
+ * accesses follow immediatly (within 0.5 usecs). They just skip the
+ * final delay nops.
+ * 
+ * Please note that accesses to SCC registers should only take place
+ * when interrupts are turned off (at least if SCC interrupts are
+ * enabled). Otherwise, an interrupt could interfere with the
+ * two-stage accessing process.
+ *
+ */
+
+static __inline__ void _SCCwrite( struct async_struct *info, int regno,
+				 unsigned char val, int final_delay )
+
+{
+	volatile struct PARTIAL_SCC *sc =
+		(volatile struct PARTIAL_SCC *)info->port;
+	unsigned char *shadow = SCC_shadow[CHANNR(info)];
+
+	switch( regno ) {
+
+	  case COMMAND_REG:
+		/* WR0 can be written directly without pointing */
+		sc->ctrl = val;
+		break;
+
+	  case SYNC_CHAR_REG:
+		/* For WR7, first set b0 of WR15 to 0, if needed */
+		if (shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT) {
+			sc->ctrl = 15;
+			shadow[INT_CTRL_REG] &= ~ICR_OPTIONREG_SELECT;
+			scc_reg3_delay();
+			sc->ctrl = shadow[INT_CTRL_REG];
+			scc_reg_delay();
+		}
+		goto normal_case;
+		
+	  case SDLC_OPTION_REG:
+		/* For WR7', first set b0 of WR15 to 1, if needed */
+		if (!(shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT)) {
+			sc->ctrl = 15;
+			shadow[INT_CTRL_REG] |= ICR_OPTIONREG_SELECT;
+			scc_reg3_delay();
+			sc->ctrl = shadow[INT_CTRL_REG];
+			scc_reg_delay();
+		}
+		sc->ctrl = 7;
+		shadow[8] = val;	/* WR7' shadowed at WR8 */
+		scc_reg3_delay();
+		sc->ctrl = val;
+		break;
+
+	  case TX_DATA_REG:		/* WR8 */
+		/* TX_DATA_REG can be accessed directly */
+		sc->data = val;
+		break;
+
+	  case MASTER_INT_CTRL:
+		sc->ctrl = regno;
+		val &= 0x3f;	/* bits 6..7 are the reset commands */
+		SCC_shadow[0][regno] = val;
+		scc_reg3_delay();
+		sc->ctrl = val;
+		break;
+
+	  case DPLL_CTRL_REG:
+		sc->ctrl = regno;
+		val &= 0x1f;			/* bits 5..7 are the DPLL commands */
+		shadow[regno] = val;
+		scc_reg3_delay();
+		sc->ctrl = val;
+		break;
+
+	  case 1 ... 6:	
+	  case 10 ... 13:
+	  case 15:
+	  normal_case:
+		sc->ctrl = regno;
+		shadow[regno] = val;
+		scc_reg3_delay();
+		sc->ctrl = val;
+		break;
+		
+	  default:
+		printk( "Bad SCC write access to WR%d\n", regno );
+		break;
+		
+	}
+
+	if (final_delay)
+		scc_reg_delay();
+}
+
+
+static __inline__ unsigned char _SCCread( struct async_struct *info,
+					 int regno, int final_delay )
+
+{	volatile struct PARTIAL_SCC *sc =
+		(volatile struct PARTIAL_SCC *)info->port;
+	unsigned char rv;
+	unsigned char *shadow = SCC_shadow[CHANNR(info)];
+
+	switch( regno ) {
+
+		/* --- real read registers --- */
+	  case STATUS_REG:
+		rv = sc->ctrl;
+		break;
+
+	  case SPCOND_STATUS_REG:
+	  case FS_FIFO_LOW_REG:
+	  case FS_FIFO_HIGH_REG:
+	  case DPLL_STATUS_REG:
+	  normal_case:
+		sc->ctrl = regno & 0x0f;
+		scc_reg_delay();
+		rv = sc->ctrl;
+		break;
+		
+	  case INT_PENDING_REG:
+		/* RR3: read only from Channel A! */
+		sc = (volatile struct PARTIAL_SCC *)
+			((info->port & ~4) ^ (ChannelsReversed << 2));
+		goto normal_case;
+
+	  case RX_DATA_REG:
+		/* RR8 can be accessed directly */
+		rv = sc->data;
+		break;
+
+	  case CURR_VECTOR_REG:
+		/* RR2 (vector including status) from Ch. B */
+		sc = (volatile struct PARTIAL_SCC *)
+			((info->port | 4) ^ (ChannelsReversed << 2));
+		goto normal_case;
+		
+		/* --- reading write registers: access the shadow --- */
+	  case 1 ... 7:
+	  case 10 ... 15:
+		return shadow[regno]; /* no final delay! */
+
+		/* WR7' is special, because it is shadowed at the place of WR8 */
+	  case SDLC_OPTION_REG:
+		return shadow[8]; /* no final delay! */
+
+		/* WR9 is special too, because it is common for both channels */
+	  case MASTER_INT_CTRL:
+		return SCC_shadow[0][9]; /* no final delay! */
+
+	  default:
+		printk( "Bad SCC read access to %cR%d\n", (regno & 16) ? 'R' : 'W',
+				regno & ~16 );
+		break;
+		
+	}
+
+	if (final_delay)
+		scc_reg_delay();
+	return rv;
+}
+
+
+#define	SCCwrite(reg,val)		_SCCwrite( info, (reg), (val), 1 )
+#define	SCCwrite_NB(reg,val)	_SCCwrite( info, (reg), (val), 0 )
+#define	SCCread(reg)			_SCCread( info, (reg), 1 )
+#define	SCCread_NB(reg)			_SCCread( info, (reg), 0 )
+
+#define SCCmod(reg,and,or)		SCCwrite((reg),(SCCread(reg)&(and))|(or))
+
+#endif /* _ATARI_SCC_H */
diff -urN pc-2.0.21/drivers/char/atarimouse.c linux-2.0.21/drivers/char/atarimouse.c
--- pc-2.0.21/drivers/char/atarimouse.c	Thu Apr 18 01:08:04 1996
+++ linux-2.0.21/drivers/char/atarimouse.c	Sat Aug 24 14:34:03 1996
@@ -20,10 +20,10 @@
 #include <linux/mm.h>
 #include <linux/random.h>
 
+#include <asm/setup.h>
 #include <asm/atarikb.h>
 #include <asm/atari_mouse.h>
 #include <asm/segment.h>
-#include <asm/bootinfo.h>
 
 static struct mouse_status mouse;
 static int atari_mouse_x_threshold = 2, atari_mouse_y_threshold = 2;
@@ -199,7 +199,7 @@
 }
 
 #ifdef MODULE
-#include <asm/bootinfo.h>
+#include <asm/setup.h>
 
 int init_module(void)
 {
diff -urN pc-2.0.21/drivers/char/fbmem.c linux-2.0.21/drivers/char/fbmem.c
--- pc-2.0.21/drivers/char/fbmem.c	Mon May 20 06:54:27 1996
+++ linux-2.0.21/drivers/char/fbmem.c	Wed Sep 18 22:22:06 1996
@@ -17,8 +17,8 @@
 #include <linux/mman.h>
 #include <linux/tty.h>
 
+#include <asm/setup.h>
 #include <asm/segment.h>
-#include <asm/bootinfo.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
 
@@ -215,10 +215,10 @@
 	vma->vm_offset += fix.smem_start;
 	if (vma->vm_offset & ~PAGE_MASK)
 		return -ENXIO;
-	if (m68k_is040or060) {
+	if (CPU_IS_040_OR_060) {
 		pgprot_val(vma->vm_page_prot) &= _CACHEMASK040;
 		/* Use write-through cache mode */
-		pgprot_val(vma->vm_page_prot) |= _PAGE_CACHE040W;
+		pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE_S;
 	}
 	if (remap_page_range(vma->vm_start, vma->vm_offset,
 			     vma->vm_end - vma->vm_start, vma->vm_page_prot))
diff -urN pc-2.0.21/drivers/char/keyb_m68k.c linux-2.0.21/drivers/char/keyb_m68k.c
--- pc-2.0.21/drivers/char/keyb_m68k.c	Fri Apr  5 16:16:48 1996
+++ linux-2.0.21/drivers/char/keyb_m68k.c	Thu Jan  1 01:00:00 1970
@@ -1,867 +0,0 @@
-/*
- * linux/drivers/char/keyboard.c
- *
- * Keyboard driver for Linux v0.99 using Latin-1.
- *
- * Written for linux by Johan Myreen as a translation from
- * the assembly version by Linus (with diacriticals added)
- *
- * Some additional features added by Christoph Niemann (ChN), March 1993
- *
- * Loadable keymaps by Risto Kankkunen, May 1993
- *
- * Diacriticals redone & other small changes, aeb@cwi.nl, June 1993
- * Added decr/incr_console, dynamic keymaps, Unicode support,
- * dynamic function/string keys, led setting,  Sept 1994
- * `Sticky' modifier keys, 951006.
- * 
- */
-
-/*
- * modified to provide 'generic' keyboard support by Hamish Macdonald
- */
-
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/mm.h>
-#include <linux/random.h>
-
-#include <asm/bitops.h>
-#include <asm/machdep.h>
-
-#include "kbd_kern.h"
-#include "diacr.h"
-#include "vt_kern.h"
-
-#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
-
-#define KBD_REPORT_ERR
-#define KBD_REPORT_UNKN
-/* #define KBD_IS_FOCUS_9000 */
-
-#ifndef KBD_DEFMODE
-#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
-#endif
-
-#ifndef KBD_DEFLEDS
-/*
- * Some laptops take the 789uiojklm,. keys as number pad when NumLock
- * is on. This seems a good reason to start with NumLock off.
- */
-#define KBD_DEFLEDS 0
-#endif
-
-#ifndef KBD_DEFLOCK
-#define KBD_DEFLOCK 0
-#endif
-
-/*
- * The default IO slowdown is doing 'inb()'s from 0x61, which should be
- * safe. But as that is the keyboard controller chip address, we do our
- * slowdowns here by doing short jumps: the keyboard controller should
- * be able to keep up
- */
-#define REALLY_SLOW_IO
-#define SLOW_IO_BY_JUMPING
-#include <asm/io.h>
-#include <asm/system.h>
-
-extern void poke_blanked_console(void);
-extern void ctrl_alt_del(void);
-extern void reset_vc(unsigned int new_console);
-extern void scrollback(int);
-extern void scrollfront(int);
-extern int vc_cons_allocated(unsigned int);
-
-unsigned char kbd_read_mask = 0x01;	/* modified by psaux.c */
-
-/*
- * global state includes the following, and various static variables
- * in this module: prev_scancode, shift_state, diacr, npadch, dead_key_next.
- * (last_console is now a global variable)
- */
-
-/* shift state counters.. */
-static unsigned char k_down[NR_SHIFT] = {0, };
-/* keyboard key bitmap */
-#define BITS_PER_LONG (8*sizeof(unsigned long))
-static unsigned long key_down[256/BITS_PER_LONG] = { 0, };
-
-static int dead_key_next = 0;
-/* 
- * In order to retrieve the shift_state (for the mouse server), either
- * the variable must be global, or a new procedure must be created to 
- * return the value. I chose the former way.
- */
-/*static*/ int shift_state = 0;
-static int npadch = -1;			/* -1 or number assembled on pad */
-static unsigned char diacr = 0;
-static char rep = 0;			/* flag telling character repeat */
-struct kbd_struct kbd_table[MAX_NR_CONSOLES];
-static struct tty_struct **ttytab;
-static struct kbd_struct *kbd = kbd_table;
-static struct tty_struct *tty = NULL;
-
-extern void compute_shiftstate(void);
-
-typedef void (*k_hand)(unsigned char value, char up_flag);
-typedef void (k_handfn)(unsigned char value, char up_flag);
-
-static k_handfn
-        do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
-	do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_ignore;
-
-static k_hand key_handler[16] = {
-	do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
-	do_meta, do_ascii, do_lock, do_lowercase, do_slock,
-	do_ignore, do_ignore, do_ignore
-};
-
-typedef void (*void_fnp)(void);
-typedef void (void_fn)(void);
-
-static void_fn do_null, enter, show_ptregs, send_intr, lastcons, caps_toggle,
-	num, hold, scroll_forw, scroll_back, boot_it, caps_on, compose,
-	SAK, decr_console, incr_console, spawn_console, bare_num;
-
-static void_fnp spec_fn_table[] = {
-	do_null,	enter,		show_ptregs,	show_mem,
-	show_state,	send_intr,	lastcons,	caps_toggle,
-	num,		hold,		scroll_forw,	scroll_back,
-	boot_it,	caps_on,	compose,	SAK,
-	decr_console,	incr_console,	spawn_console,	bare_num
-};
-
-/* maximum values each key_handler can handle */
-const int max_vals[] = {
-	255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1,
-	NR_DEAD - 1, 255, 3, NR_SHIFT - 1,
-	255, NR_ASCII - 1, NR_LOCK - 1, 255,
-	NR_LOCK - 1
-};
-
-const int NR_TYPES = SIZE(max_vals);
-
-static void put_queue(int);
-static unsigned char handle_diacr(unsigned char);
-
-/*
- * Many other routines do put_queue, but I think either
- * they produce ASCII, or they produce some user-assigned
- * string, and in both cases we might assume that it is
- * in utf-8 already.
- */
-void to_utf8(ushort c) {
-    if (c < 0x80)
-	put_queue(c);			/*  0*******  */
-    else if (c < 0x800) {
-	put_queue(0xc0 | (c >> 6)); 	/*  110***** 10******  */
-	put_queue(0x80 | (c & 0x3f));
-    } else {
-	put_queue(0xe0 | (c >> 12)); 	/*  1110**** 10****** 10******  */
-	put_queue(0x80 | ((c >> 6) & 0x3f));
-	put_queue(0x80 | (c & 0x3f));
-    }
-    /* UTF-8 is defined for words of up to 31 bits,
-       but we need only 16 bits here */
-}
-
-void process_keycode (int keycode)
-{
-	char up_flag;		                 /* 0 or 0200 */
-	char raw_mode;
-
-	do_poke_blanked_console = 1;
-	mark_bh(KEYBOARD_BH);
-	add_keyboard_randomness(keycode);
-
-	tty = ttytab[fg_console];
- 	kbd = kbd_table + fg_console;
-	if ((raw_mode = (kbd->kbdmode == VC_RAW))) {
- 		put_queue(keycode);
-		/* we do not return yet, because we want to maintain
-		   the key_down array, so that we have the correct
-		   values when finishing RAW mode or when changing VT's */
- 	}
-
-	/*
-	 * At this point the variable `keycode' contains the keycode.
-	 * Note: the keycode must not be 0.
-	 * We keep track of the up/down status of the key, and
-	 * return the keycode if in MEDIUMRAW mode.
-	 */
-
-	up_flag = (keycode & 0200);
-        keycode &= 0x7f;
-	if (up_flag) {
-		rep = 0;
- 		if(!clear_bit(keycode, key_down)) {
-		    /* unexpected, but this can happen:
-		       maybe this was a key release for a FOCUS 9000
-		       PF key; if we want to see it, we have to clear
-		       up_flag */
-#ifndef __mc68000__
-		    if (keycode >= SC_LIM || keycode == 85)
-		      up_flag = 0;
-#endif
-		}
-	} else
- 		rep = set_bit(keycode, key_down);
-
-	if (raw_mode)
-	        return;
-
-	if (kbd->kbdmode == VC_MEDIUMRAW) {
-		/* soon keycodes will require more than one byte */
- 		put_queue(keycode + up_flag);
-		return;
-	}
-
- 	/*
-	 * Small change in philosophy: earlier we defined repetition by
-	 *	 rep = keycode == prev_keycode;
-	 *	 prev_keycode = keycode;
-	 * but now by the fact that the depressed key was down already.
-	 * Does this ever make a difference? Yes.
-	 */
-
-	/*
- 	 *  Repeat a key only if the input buffers are empty or the
- 	 *  characters get echoed locally. This makes key repeat usable
- 	 *  with slow applications and under heavy loads.
-	 */
-	if (!rep ||
-	    (vc_kbd_mode(kbd,VC_REPEAT) && tty &&
-	     (L_ECHO(tty) || (tty->driver.chars_in_buffer(tty) == 0)))) {
-		u_short keysym;
-		u_char type;
-
-		/* the XOR below used to be an OR */
-		int shift_final = shift_state ^ kbd->lockstate ^ kbd->slockstate;
-		ushort *key_map = key_maps[shift_final];
-
-		if (key_map != NULL) {
-			keysym = key_map[keycode];
-			type = KTYP(keysym);
-
-			if (type >= 0xf0) {
-			    type -= 0xf0;
-			    if (type == KT_LETTER) {
-				type = KT_LATIN;
-				if (vc_kbd_led(kbd, VC_CAPSLOCK)) {
-				    key_map = key_maps[shift_final ^ (1<<KG_SHIFT)];
-				    if (key_map)
-				      keysym = key_map[keycode];
-				}
-			    }
-			    (*key_handler[type])(keysym & 0xff, up_flag);
-			    if (type != KT_SLOCK)
-			      kbd->slockstate = 0;
-			} else {
-			    /* maybe only if (kbd->kbdmode == VC_UNICODE) ? */
-			    if (!up_flag)
-			      to_utf8(keysym);
-			}
-		} else {
-			/* maybe beep? */
-			/* we have at least to update shift_state */
-#if 1			/* how? two almost equivalent choices follow */
-			compute_shiftstate();
-#else
-			keysym = U(plain_map[keycode]);
-			type = KTYP(keysym);
-			if (type == KT_SHIFT)
-			  (*key_handler[type])(keysym & 0xff, up_flag);
-#endif
-		}
-        }
-}
-
-static void put_queue(int ch)
-{
-	wake_up(&keypress_wait);
-	if (tty) {
-		tty_insert_flip_char(tty, ch, 0);
-		tty_schedule_flip(tty);
-	}
-}
-
-static void puts_queue(char *cp)
-{
-	wake_up(&keypress_wait);
-	if (!tty)
-		return;
-
-	while (*cp) {
-		tty_insert_flip_char(tty, *cp, 0);
-		cp++;
-	}
-	tty_schedule_flip(tty);
-}
-
-static void applkey(int key, char mode)
-{
-	static char buf[] = { 0x1b, 'O', 0x00, 0x00 };
-
-	buf[1] = (mode ? 'O' : '[');
-	buf[2] = key;
-	puts_queue(buf);
-}
-
-static void enter(void)
-{
-	put_queue(13);
-	if (vc_kbd_mode(kbd,VC_CRLF))
-		put_queue(10);
-}
-
-static void caps_toggle(void)
-{
-	if (rep)
-		return;
-	chg_vc_kbd_led(kbd, VC_CAPSLOCK);
-}
-
-static void caps_on(void)
-{
-	if (rep)
-		return;
-	set_vc_kbd_led(kbd, VC_CAPSLOCK);
-}
-
-struct pt_regs *pt_regs;
-
-static void show_ptregs(void)
-{
-	if (pt_regs)
-		show_regs(pt_regs);
-	return;
-}
-
-static void hold(void)
-{
-	if (rep || !tty)
-		return;
-
-	/*
-	 * Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty);
-	 * these routines are also activated by ^S/^Q.
-	 * (And SCROLLOCK can also be set by the ioctl KDSKBLED.)
-	 */
-	if (tty->stopped)
-		start_tty(tty);
-	else
-		stop_tty(tty);
-}
-
-static void num(void)
-{
-	if (vc_kbd_mode(kbd,VC_APPLIC))
-		applkey('P', 1);
-	else
-		bare_num();
-}
-
-/*
- * Bind this to Shift-NumLock if you work in application keypad mode
- * but want to be able to change the NumLock flag.
- * Bind this to NumLock if you prefer that the NumLock key always
- * changes the NumLock flag.
- */
-static void bare_num(void)
-{
-	if (!rep)
-		chg_vc_kbd_led(kbd,VC_NUMLOCK);
-}
-
-static void lastcons(void)
-{
-	/* switch to the last used console, ChN */
-	set_console(last_console);
-}
-
-static void decr_console(void)
-{
-	int i;
- 
-	for (i = fg_console-1; i != fg_console; i--) {
-		if (i == -1)
-			i = MAX_NR_CONSOLES-1;
-		if (vc_cons_allocated(i))
-			break;
-	}
-	set_console(i);
-}
-
-static void incr_console(void)
-{
-	int i;
-
-	for (i = fg_console+1; i != fg_console; i++) {
-		if (i == MAX_NR_CONSOLES)
-			i = 0;
-		if (vc_cons_allocated(i))
-			break;
-	}
-	set_console(i);
-}
-
-static void send_intr(void)
-{
-	if (!tty)
-		return;
-	tty_insert_flip_char(tty, 0, TTY_BREAK);
-	tty_schedule_flip(tty);
-}
-
-static void scroll_forw(void)
-{
-	scrollfront(0);
-}
-
-static void scroll_back(void)
-{
-	scrollback(0);
-}
-
-static void boot_it(void)
-{
-	ctrl_alt_del();
-}
-
-static void compose(void)
-{
-	dead_key_next = 1;
-}
-
-int spawnpid, spawnsig;
-
-static void spawn_console(void)
-{
-        if (spawnpid)
-	   if(kill_proc(spawnpid, spawnsig, 1))
-	     spawnpid = 0;
-}
-
-static void SAK(void)
-{
-	do_SAK(tty);
-#if 0
-	/*
-	 * Need to fix SAK handling to fix up RAW/MEDIUM_RAW and
-	 * vt_cons modes before we can enable RAW/MEDIUM_RAW SAK
-	 * handling.
-	 * 
-	 * We should do this some day --- the whole point of a secure
-	 * attention key is that it should be guaranteed to always
-	 * work.
-	 */
-	reset_vc(fg_console);
-	do_unblank_screen();	/* not in interrupt routine? */
-#endif
-}
-
-static void do_ignore(unsigned char value, char up_flag)
-{
-}
-
-static void do_null()
-{
-	compute_shiftstate();
-}
-
-static void do_spec(unsigned char value, char up_flag)
-{
-	if (up_flag)
-		return;
-	if (value >= SIZE(spec_fn_table))
-		return;
-	spec_fn_table[value]();
-}
-
-static void do_lowercase(unsigned char value, char up_flag)
-{
-	printk("keyboard.c: do_lowercase was called - impossible\n");
-}
-
-static void do_self(unsigned char value, char up_flag)
-{
-	if (up_flag)
-		return; 	/* no action, if this is a key release */
-
-	if (diacr)
-		value = handle_diacr(value);
-
-        if (dead_key_next) {
-                dead_key_next = 0;
-                diacr = value;
-                return;
-        }
-
-	put_queue(value);
-}
-
-#define A_GRAVE  '`'
-#define A_ACUTE  '\''
-#define A_CFLEX  '^'
-#define A_TILDE  '~'
-#define A_DIAER  '"'
-static unsigned char ret_diacr[] =
-        {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER };
-
-/* If a dead key pressed twice, output a character corresponding to it, */
-/* otherwise just remember the dead key.				*/
-
-static void do_dead(unsigned char value, char up_flag)
-{
-	if (up_flag)
-		return;
-
-        value = ret_diacr[value];
-        if (diacr == value) {   /* pressed twice */
-                diacr = 0;
-                put_queue(value);
-                return;
-        }
-	diacr = value;
-}
-
-
-/* If space is pressed, return the character corresponding the pending	*/
-/* dead key, otherwise try to combine the two.				*/
-
-unsigned char handle_diacr(unsigned char ch)
-{
-        int d = diacr;
-        int i;
-
-        diacr = 0;
-        if (ch == ' ')
-                return d;
-
-	for (i = 0; i < accent_table_size; i++) {
-		if (accent_table[i].diacr == d && accent_table[i].base == ch)
-			return accent_table[i].result;
-	}
-
-        put_queue(d);
-        return ch;
-}
-
-static void do_cons(unsigned char value, char up_flag)
-{
-	if (up_flag)
-		return;
-	set_console(value);
-}
-
-static void do_fn(unsigned char value, char up_flag)
-{
-	if (up_flag)
-		return;
-	if (value < SIZE(func_table)) {
-		if (func_table[value])
-			puts_queue(func_table[value]);
-	} else
-	        printk("do_fn called with value=%d\n", value);
-}
-
-static void do_pad(unsigned char value, char up_flag)
-{
-	static const char *pad_chars = "0123456789+-*/\015,.?";
-	static const char *app_map = "pqrstuvwxylSRQMnn?";
-
-	if (up_flag)
-		return;		/* no action, if this is a key release */
-
-	/* kludge... shift forces cursor/number keys */
-	if (vc_kbd_mode(kbd,VC_APPLIC) && !k_down[KG_SHIFT]) {
-		applkey(app_map[value], 1);
-		return;
-	}
-
-	if (!vc_kbd_led(kbd,VC_NUMLOCK))
-		switch (value) {
-			case KVAL(K_PCOMMA):
-			case KVAL(K_PDOT):
-				do_fn(KVAL(K_REMOVE), 0);
-				return;
-			case KVAL(K_P0):
-				do_fn(KVAL(K_INSERT), 0);
-				return;
-			case KVAL(K_P1):
-				do_fn(KVAL(K_SELECT), 0);
-				return;
-			case KVAL(K_P2):
-				do_cur(KVAL(K_DOWN), 0);
-				return;
-			case KVAL(K_P3):
-				do_fn(KVAL(K_PGDN), 0);
-				return;
-			case KVAL(K_P4):
-				do_cur(KVAL(K_LEFT), 0);
-				return;
-			case KVAL(K_P6):
-				do_cur(KVAL(K_RIGHT), 0);
-				return;
-			case KVAL(K_P7):
-				do_fn(KVAL(K_FIND), 0);
-				return;
-			case KVAL(K_P8):
-				do_cur(KVAL(K_UP), 0);
-				return;
-			case KVAL(K_P9):
-				do_fn(KVAL(K_PGUP), 0);
-				return;
-			case KVAL(K_P5):
-				applkey('G', vc_kbd_mode(kbd, VC_APPLIC));
-				return;
-		}
-
-	put_queue(pad_chars[value]);
-	if (value == KVAL(K_PENTER) && vc_kbd_mode(kbd, VC_CRLF))
-		put_queue(10);
-}
-
-static void do_cur(unsigned char value, char up_flag)
-{
-	static const char *cur_chars = "BDCA";
-	if (up_flag)
-		return;
-
-	applkey(cur_chars[value], vc_kbd_mode(kbd,VC_CKMODE));
-}
-
-static void do_shift(unsigned char value, char up_flag)
-{
-	int old_state = shift_state;
-
-	if (rep)
-		return;
-
-	/* Mimic typewriter:
-	   a CapsShift key acts like Shift but undoes CapsLock */
-	if (value == KVAL(K_CAPSSHIFT)) {
-		value = KVAL(K_SHIFT);
-		if (!up_flag)
-			clr_vc_kbd_led(kbd, VC_CAPSLOCK);
-	}
-
-	if (up_flag) {
-	        /* handle the case that two shift or control
-		   keys are depressed simultaneously */
-		if (k_down[value])
-			k_down[value]--;
-	} else
-		k_down[value]++;
-
-	if (k_down[value])
-		shift_state |= (1 << value);
-	else
-		shift_state &= ~ (1 << value);
-
-	/* kludge */
-	if (up_flag && shift_state != old_state && npadch != -1) {
-		if (kbd->kbdmode == VC_UNICODE)
-		  to_utf8(npadch & 0xffff);
-		else
-		  put_queue(npadch & 0xff);
-		npadch = -1;
-	}
-}
-
-/* called after returning from RAW mode or when changing consoles -
-   recompute k_down[] and shift_state from key_down[] */
-/* maybe called when keymap is undefined, so that shiftkey release is seen */
-void compute_shiftstate(void)
-{
-	int i, j, k, sym, val;
-
-	shift_state = 0;
-	for(i=0; i < SIZE(k_down); i++)
-	  k_down[i] = 0;
-
-	for(i=0; i < SIZE(key_down); i++)
-	  if(key_down[i]) {	/* skip this word if not a single bit on */
-	    k = i*BITS_PER_LONG;
-	    for(j=0; j<BITS_PER_LONG; j++,k++)
-	      if(test_bit(k, key_down)) {
-		sym = U(plain_map[k]);
-		if(KTYP(sym) == KT_SHIFT) {
-		  val = KVAL(sym);
-		  if (val == KVAL(K_CAPSSHIFT))
-		    val = KVAL(K_SHIFT);
-		  k_down[val]++;
-		  shift_state |= (1<<val);
-	        }
-	      }
-	  }
-}
-
-static void do_meta(unsigned char value, char up_flag)
-{
-	if (up_flag)
-		return;
-
-	if (vc_kbd_mode(kbd, VC_META)) {
-		put_queue('\033');
-		put_queue(value);
-	} else
-		put_queue(value | 0x80);
-}
-
-static void do_ascii(unsigned char value, char up_flag)
-{
-	int base;
-
-	if (up_flag)
-		return;
-
-	if (value < 10)    /* decimal input of code, while Alt depressed */
-	    base = 10;
-	else {       /* hexadecimal input of code, while AltGr depressed */
-	    value -= 10;
-	    base = 16;
-	}
-
-	if (npadch == -1)
-	        npadch = value;
-	else
-		npadch = npadch * base + value;
-}
-
-static void do_lock(unsigned char value, char up_flag)
-{
-	if (up_flag || rep)
-		return;
-	chg_vc_kbd_lock(kbd, value);
-}
-
-static void do_slock(unsigned char value, char up_flag)
-{
-	if (up_flag || rep)
-		return;
-	chg_vc_kbd_slock(kbd, value);
-}
-
-/*
- * The leds display either (i) the status of NumLock, CapsLock, ScrollLock,
- * or (ii) whatever pattern of lights people want to show using KDSETLED,
- * or (iii) specified bits of specified words in kernel memory.
- */
-
-static unsigned char ledstate = 0xff; /* undefined */
-static unsigned char ledioctl;
-
-unsigned char getledstate(void) {
-    return ledstate;
-}
-
-void setledstate(struct kbd_struct *kbd, unsigned int led) {
-    if (!(led & ~7)) {
-	ledioctl = led;
-	kbd->ledmode = LED_SHOW_IOCTL;
-    } else
-	kbd->ledmode = LED_SHOW_FLAGS;
-    set_leds();
-}
-
-static struct ledptr {
-    unsigned int *addr;
-    unsigned int mask;
-    unsigned char valid:1;
-} ledptrs[3];
-
-void register_leds(int console, unsigned int led,
-		   unsigned int *addr, unsigned int mask) {
-    struct kbd_struct *kbd = kbd_table + console;
-    if (led < 3) {
-	ledptrs[led].addr = addr;
-	ledptrs[led].mask = mask;
-	ledptrs[led].valid = 1;
-	kbd->ledmode = LED_SHOW_MEM;
-    } else
-	kbd->ledmode = LED_SHOW_FLAGS;
-}
-
-static inline unsigned char getleds(void){
-    struct kbd_struct *kbd = kbd_table + fg_console;
-    unsigned char leds;
-
-    if (kbd->ledmode == LED_SHOW_IOCTL)
-      return ledioctl;
-    leds = kbd->ledflagstate;
-    if (kbd->ledmode == LED_SHOW_MEM) {
-	if (ledptrs[0].valid) {
-	    if (*ledptrs[0].addr & ledptrs[0].mask)
-	      leds |= 1;
-	    else
-	      leds &= ~1;
-	}
-	if (ledptrs[1].valid) {
-	    if (*ledptrs[1].addr & ledptrs[1].mask)
-	      leds |= 2;
-	    else
-	      leds &= ~2;
-	}
-	if (ledptrs[2].valid) {
-	    if (*ledptrs[2].addr & ledptrs[2].mask)
-	      leds |= 4;
-	    else
-	      leds &= ~4;
-	}
-    }
-    return leds;
-}
-
-/*
- * This routine is the bottom half of the keyboard interrupt
- * routine, and runs with all interrupts enabled. It does
- * console changing, led setting and copy_to_cooked, which can
- * take a reasonably long time.
- *
- * Aside from timing (which isn't really that important for
- * keyboard interrupts as they happen often), using the software
- * interrupt routines for this thing allows us to easily mask
- * this when we don't want any of the above to happen. Not yet
- * used, but this allows for easy and efficient race-condition
- * prevention later on.
- */
-static void kbd_bh(void)
-{
-	unsigned char leds = getleds();
-
-	if (leds != ledstate) {
-		ledstate = leds;
-		if (mach_kbd_leds)
-			mach_kbd_leds(leds);
-	}
-}
-
-int kbd_init(void)
-{
-	int i;
-	struct kbd_struct kbd0;
-	extern struct tty_driver console_driver;
-
-	kbd0.ledflagstate = kbd0.default_ledflagstate = KBD_DEFLEDS;
-	kbd0.ledmode = LED_SHOW_FLAGS;
-	kbd0.lockstate = KBD_DEFLOCK;
-	kbd0.slockstate = 0;
-	kbd0.modeflags = KBD_DEFMODE;
-	kbd0.kbdmode = VC_XLATE;
- 
-	for (i = 0 ; i < MAX_NR_CONSOLES ; i++)
-		kbd_table[i] = kbd0;
-
-	ttytab = console_driver.table;
-
-	init_bh(KEYBOARD_BH, kbd_bh);
-	mark_bh(KEYBOARD_BH);
-
-	mach_keyb_init ();
-
-	return 0;
-}
diff -urN pc-2.0.21/drivers/char/keyboard.c linux-2.0.21/drivers/char/keyboard.c
--- pc-2.0.21/drivers/char/keyboard.c	Thu Jul 25 19:24:49 1996
+++ linux-2.0.21/drivers/char/keyboard.c	Sat Aug 24 14:34:43 1996
@@ -15,6 +15,8 @@
  * dynamic function/string keys, led setting,  Sept 1994
  * `Sticky' modifier keys, 951006.
  * 
+ * Modified to provide 'generic' keyboard support by Hamish Macdonald
+ * Merge with the m68k keyboard driver by Geert Uytterhoeven
  */
 
 #define KEYBOARD_IRQ 1
@@ -37,13 +39,24 @@
 #include "diacr.h"
 #include "vt_kern.h"
 
+#if defined(__i386__) || defined(__alpha__)
+#define TRANSLATE_SCANCODES	1
+#define USE_MACHDEP_ABSTRACTION	0
+#endif /* __i386__ || __alpha__ */
+
+#if defined(__mc68000__)
+#define TRANSLATE_SCANCODES	0
+#define USE_MACHDEP_ABSTRACTION	1
+#include <asm/machdep.h>
+#endif /* __mc68000__ */
+
 /*
- * On non-x86 hardware we do a full keyboard controller
+ * On non-x86 and non-m68k hardware we do a full keyboard controller
  * initialization, in case the bootup software hasn't done
  * it. On a x86, the BIOS will already have initialized the
- * keyboard.
+ * keyboard. On m68k we use the machdep abstraction (hint, hint... ;-)
  */
-#ifndef __i386__
+#if !defined(__i386__) && !USE_MACHDEP_ABSTRACTION
 #define INIT_KBD
 static int initialize_kbd(void);
 #endif
@@ -108,10 +121,12 @@
 static struct kbd_struct * kbd = kbd_table;
 static struct tty_struct * tty = NULL;
 
+#if !USE_MACHDEP_ABSTRACTION
 /* used only by send_data - set by keyboard_interrupt */
 static volatile unsigned char reply_expected = 0;
 static volatile unsigned char acknowledge = 0;
 static volatile unsigned char resend = 0;
+#endif /* !USE_MACHDEP_ABSTRACTION */
 
 extern void compute_shiftstate(void);
 
@@ -157,23 +172,7 @@
 static unsigned char handle_diacr(unsigned char);
 
 /* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
-static struct pt_regs * pt_regs;
-
-static inline void kb_wait(void)
-{
-	int i;
-
-	for (i=0; i<0x100000; i++)
-		if ((inb_p(0x64) & 0x02) == 0)
-			return;
-	printk(KERN_WARNING "Keyboard timed out\n");
-}
-
-static inline void send_cmd(unsigned char c)
-{
-	kb_wait();
-	outb(c,0x64);
-}
+struct pt_regs * pt_regs;
 
 /*
  * Many other routines do put_queue, but I think either
@@ -196,6 +195,7 @@
        but we need only 16 bits here */
 }
 
+#if TRANSLATE_SCANCODES
 /*
  * Translation of escaped scancodes to keycodes.
  * This is now user-settable.
@@ -330,21 +330,30 @@
 	    e0_keys[scancode - 128];
 }
 
-#if DISABLE_KBD_DURING_INTERRUPTS
-#define disable_keyboard()	do { send_cmd(0xAD); kb_wait(); } while (0)
-#define enable_keyboard()	send_cmd(0xAE)
-#else
-#define disable_keyboard()	/* nothing */
-#define enable_keyboard()	/* nothing */
-#endif
+#else /* TRANSLATE_SCANCODES */
+
+int setkeycode(unsigned int scancode, unsigned int keycode)
+{
+	return -EOPNOTSUPP;
+}
+
+int getkeycode(unsigned int scancode)
+{
+	return -EOPNOTSUPP;
+}
+
+#endif /* TRANSLATE_SCANCODES */
 
-static void handle_scancode(unsigned char scancode)
+void handle_scancode(unsigned char scancode)
 {
 	unsigned char keycode;
+#if TRANSLATE_SCANCODES
 	static unsigned int prev_scancode = 0;   /* remember E0, E1 */
+#endif /* TRANSLATE_SCANCODES */
 	char up_flag;				 /* 0 or 0200 */
 	char raw_mode;
 
+#if !USE_MACHDEP_ABSTRACTION
 	if (reply_expected) {
 	  /* 0xfa, 0xfe only mean "acknowledge", "resend" for most keyboards */
 	  /* but they are the key-up scancodes for PF6, PF10 on a FOCUS 9000 */
@@ -370,6 +379,7 @@
 		prev_scancode = 0;
 		return;
 	}
+#endif /* !USE_MACHDEP_ABSTRACTION */
 	do_poke_blanked_console = 1;
 	mark_bh(CONSOLE_BH);
 	add_keyboard_randomness(scancode);
@@ -383,6 +393,7 @@
 		   values when finishing RAW mode or when changing VT's */
  	}
 
+#if TRANSLATE_SCANCODES
 	if (scancode == 0xff) {
 	        /* in scancode mode 1, my ESC key generates 0xff */
 		/* the calculator keys on a FOCUS 9000 generate 0xff */
@@ -400,6 +411,7 @@
 		prev_scancode = scancode;
 		return;
  	}
+#endif /* TRANSLATE_SCANCODES */
 
  	/*
 	 *  Convert scancode to keycode, using prev_scancode.
@@ -407,6 +419,7 @@
 	up_flag = (scancode & 0200);
  	scancode &= 0x7f;
 
+#if TRANSLATE_SCANCODES
 	if (prev_scancode) {
 	  /*
 	   * usually it will be 0xe0, but a Pause key generates
@@ -479,6 +492,7 @@
 	      return;
 	  }
  	} else
+#endif /* TRANSLATE_SCANCODES */
 	  keycode = scancode;
 
 	/*
@@ -491,12 +505,14 @@
 	if (up_flag) {
 		rep = 0;
  		if(!clear_bit(keycode, key_down)) {
+#if !USE_MACHDEP_ABSTRACTION
 		    /* unexpected, but this can happen:
 		       maybe this was a key release for a FOCUS 9000
 		       PF key; if we want to see it, we have to clear
 		       up_flag */
 		    if (keycode >= SC_LIM || keycode == 85)
 		      up_flag = 0;
+#endif /* !USE_MACHDEP_ABSTRACTION */
 		}
 	} else
  		rep = set_bit(keycode, key_down);
@@ -570,6 +586,31 @@
 	}
 }
 
+#if !USE_MACHDEP_ABSTRACTION
+static inline void kb_wait(void)
+{
+	int i;
+
+	for (i=0; i<0x100000; i++)
+		if ((inb_p(0x64) & 0x02) == 0)
+			return;
+	printk(KERN_WARNING "Keyboard timed out\n");
+}
+
+static inline void send_cmd(unsigned char c)
+{
+	kb_wait();
+	outb(c,0x64);
+}
+
+#if DISABLE_KBD_DURING_INTERRUPTS
+#define disable_keyboard()	do { send_cmd(0xAD); kb_wait(); } while (0)
+#define enable_keyboard()	send_cmd(0xAE)
+#else
+#define disable_keyboard()	/* nothing */
+#define enable_keyboard()	/* nothing */
+#endif
+
 static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	unsigned char status;
@@ -596,6 +637,36 @@
 	enable_keyboard();
 }
 
+/*
+ * send_data sends a character to the keyboard and waits
+ * for a acknowledge, possibly retrying if asked to. Returns
+ * the success status.
+ */
+static int send_data(unsigned char data)
+{
+	int retries = 3;
+	int i;
+
+	do {
+		kb_wait();
+		acknowledge = 0;
+		resend = 0;
+		reply_expected = 1;
+		outb_p(data, 0x60);
+		for(i=0; i<0x200000; i++) {
+			inb_p(0x64);		/* just as a delay */
+			if (acknowledge)
+				return 1;
+			if (resend)
+				break;
+		}
+		if (!resend)
+			return 0;
+	} while (retries-- > 0);
+	return 0;
+}
+#endif /* !USE_MACHDEP_ABSTRACTION */
+
 static void put_queue(int ch)
 {
 	wake_up(&keypress_wait);
@@ -1061,35 +1132,6 @@
 }
 
 /*
- * send_data sends a character to the keyboard and waits
- * for a acknowledge, possibly retrying if asked to. Returns
- * the success status.
- */
-static int send_data(unsigned char data)
-{
-	int retries = 3;
-	int i;
-
-	do {
-		kb_wait();
-		acknowledge = 0;
-		resend = 0;
-		reply_expected = 1;
-		outb_p(data, 0x60);
-		for(i=0; i<0x200000; i++) {
-			inb_p(0x64);		/* just as a delay */
-			if (acknowledge)
-				return 1;
-			if (resend)
-				break;
-		}
-		if (!resend)
-			return 0;
-	} while (retries-- > 0);
-	return 0;
-}
-
-/*
  * The leds display either (i) the status of NumLock, CapsLock, ScrollLock,
  * or (ii) whatever pattern of lights people want to show using KDSETLED,
  * or (iii) specified bits of specified words in kernel memory.
@@ -1178,8 +1220,13 @@
 
 	if (leds != ledstate) {
 		ledstate = leds;
+#if USE_MACHDEP_ABSTRACTION
+		if (mach_kbd_leds)
+			mach_kbd_leds(leds);
+#else /* USE_MACHDEP_ABSTRACTION */
 		if (!send_data(0xed) || !send_data(leds))
 			send_data(0xf4);	/* re-enable kbd if any errors */
+#endif /* USE_MACHDEP_ABSTRACTION */
 	}
 }
 
@@ -1201,11 +1248,15 @@
 
 	ttytab = console_driver.table;
 
+#if USE_MACHDEP_ABSTRACTION
+	mach_keyb_init();
+#else /* USE_MACHDEP_ABSTRACTION */
 	request_irq(KEYBOARD_IRQ, keyboard_interrupt, 0, "keyboard", NULL);
 	request_region(0x60,16,"keyboard");
 #ifdef INIT_KBD
 	initialize_kbd();
 #endif
+#endif /* USE_MACHDEP_ABSTRACTION */
 	init_bh(KEYBOARD_BH, kbd_bh);
 	mark_bh(KEYBOARD_BH);
 	return 0;
diff -urN pc-2.0.21/drivers/char/lp_intern.c linux-2.0.21/drivers/char/lp_intern.c
--- pc-2.0.21/drivers/char/lp_intern.c	Fri Apr 26 11:12:25 1996
+++ linux-2.0.21/drivers/char/lp_intern.c	Thu Sep 26 23:29:03 1996
@@ -14,27 +14,31 @@
  * Copyright (C) 1993 by Nigel Gamble (added interrupt code)
  */
 
+#include <linux/module.h>
 #include <linux/config.h>
-#include <linux/lp_intern.h>
 #include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <asm/irq.h>
 #ifdef CONFIG_AMIGA
 #include <asm/amigahw.h>
+#include <asm/amigaints.h>
 #endif
 #ifdef CONFIG_ATARI
 #include <linux/delay.h>
 #include <linux/sched.h>
-#include <linux/interrupt.h>
 #include <asm/atarihw.h>
+#include <asm/atariints.h>
 #endif
+#include <linux/lp_intern.h>
 
+static int my_inter = 0;
+static int minor;
 
 static void lp_int_out(int, int);
 static int lp_int_busy(int);
 static int lp_int_pout(int);
 static int lp_int_online(int);
-static int lp_int_interrupt(int);
-
-int lp_internal_init(struct lp_struct *, int, int, int);
 
 
 static void
@@ -46,7 +50,7 @@
     case MACH_AMIGA:
        {
 	int wait = 0;
-	while (wait != lp_table[dev].wait) wait++;
+	while (wait != lp_table[dev]->wait) wait++;
 	ciaa.prb = c;
        }
       break;
@@ -58,7 +62,7 @@
 	 sound_ym.rd_data_reg_sel = 15;
 	 sound_ym.wd_data = c;
 	 sound_ym.rd_data_reg_sel = 14;
-	 while (wait != lp_table[dev].wait) wait++;
+	 while (wait != lp_table[dev]->wait) wait++;
 	 sound_ym.wd_data = sound_ym.rd_data_reg_sel & ~(1 << 5);
 	 while (wait) wait--;
 	 sound_ym.wd_data = sound_ym.rd_data_reg_sel | (1 << 5);
@@ -121,21 +125,59 @@
     }
 }
 
-static int lp_int_interrupt(int dev)
+static int lp_int_my_interrupt(int dev)
+{
+  return my_inter;
+}
+
+static void lp_int_interrupt(int irq, void *data, struct pt_regs *fp)
+{
+  my_inter = 1;
+  lp_interrupt(irq, data, fp);
+  my_inter = 0;
+}
+
+static void lp_int_open(void)
+{
+  MOD_INC_USE_COUNT;
+}
+
+static void lp_int_release(void)
 {
-  return 1;
+  MOD_DEC_USE_COUNT;
 }
 
-int lp_internal_init(struct lp_struct *lp_table, int entry,
-		     int max_lp, int irq)
+static struct lp_struct tab = {
+	"Builtin parallel port",	/* name */
+	0,				/* irq */
+	lp_int_out,
+	lp_int_busy,
+	lp_int_pout,
+	lp_int_online,
+	lp_int_my_interrupt,
+	NULL,				/* ioctl */
+	lp_int_open,
+	lp_int_release,
+	LP_EXIST,
+	LP_INIT_CHAR,
+	LP_INIT_TIME,
+	LP_INIT_WAIT,
+	NULL,
+	NULL,
+};
+
+int lp_internal_init(void)
 {
-  if (max_lp-entry < 1)
-    return 0;
 #ifdef CONFIG_AMIGA
-  if (MACH_IS_AMIGA)
+  if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_PARALLEL))
     {
       ciaa.ddrb = 0xff;
       ciab.ddra &= 0xf8;
+      if (lp_irq)
+        tab.irq = request_irq(IRQ_AMIGA_CIAA_FLG, lp_int_interrupt,
+          0, "builtin printer port", lp_int_interrupt);
+      tab.base = (void *) &ciaa.prb; /* dummy, not used */
+      tab.type = LP_AMIGA;
     }
 #endif
 #ifdef CONFIG_ATARI
@@ -148,22 +190,50 @@
       sound_ym.rd_data_reg_sel = 7;
       sound_ym.wd_data = (sound_ym.rd_data_reg_sel & 0x3f) | 0xc0;
       restore_flags(flags);
+      if (lp_irq)
+        tab.irq = request_irq(IRQ_MFP_BUSY, lp_int_interrupt,
+          IRQ_TYPE_SLOW, "builtin printer port", lp_int_interrupt);
+      tab.base = (void *) &sound_ym.wd_data; /* dummy, not used */
+      tab.type = LP_ATARI;
     }
 #endif
-  lp_table[entry].name = "Builtin LP";
-  lp_table[entry].lp_out = lp_int_out;
-  lp_table[entry].lp_is_busy = lp_int_busy;
-  lp_table[entry].lp_has_pout = lp_int_pout;
-  lp_table[entry].lp_is_online = lp_int_online;
-  lp_table[entry].lp_my_interrupt = lp_int_interrupt;
-  lp_table[entry].flags = LP_EXIST;
-  lp_table[entry].chars = LP_INIT_CHAR;
-  lp_table[entry].time = LP_INIT_TIME;
-  lp_table[entry].wait = LP_INIT_WAIT;
-  lp_table[entry].lp_wait_q = NULL;
 
-  printk("lp%d: internal port\n", entry);
+  if ((minor = register_parallel(&tab, -1)) < 0) {
+    printk("builtin lp init: cant get a minor\n");
+    if (lp_irq) {
+#ifdef CONFIG_AMIGA
+      if (MACH_IS_AMIGA)
+	free_irq(IRQ_AMIGA_CIAA_FLG, lp_int_interrupt);
+#endif
+#ifdef CONFIG_ATARI
+      if (MACH_IS_ATARI)
+	free_irq(IRQ_MFP_BUSY, lp_int_interrupt);
+#endif
+    }
+    return -ENODEV;
+  }
+  
+  return 0;
+}
 
-  return 1;
+#ifdef MODULE
+int init_module(void)
+{
+return lp_internal_init();
 }
 
+void cleanup_module(void)
+{
+if (lp_irq) {
+#ifdef CONFIG_AMIGA
+  if (MACH_IS_AMIGA)
+    free_irq(IRQ_AMIGA_CIAA_FLG, lp_int_interrupt);
+#endif
+#ifdef CONFIG_ATARI
+  if (MACH_IS_ATARI)
+    free_irq(IRQ_MFP_BUSY, lp_int_interrupt);
+#endif
+}
+unregister_parallel(minor);
+}
+#endif
diff -urN pc-2.0.21/drivers/char/lp_m68k.c linux-2.0.21/drivers/char/lp_m68k.c
--- pc-2.0.21/drivers/char/lp_m68k.c	Mon May  6 11:49:43 1996
+++ linux-2.0.21/drivers/char/lp_m68k.c	Thu Sep 26 17:10:43 1996
@@ -34,23 +34,23 @@
  *
  */
 
+#include <linux/module.h>
 #include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/sched.h>
+#include <linux/string.h>
 #include <asm/irq.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
 
 #ifdef CONFIG_AMIGA
-#include <asm/amigaints.h>
 #ifdef CONFIG_MULTIFACE_III_LP
 #include <linux/lp_mfc.h>
 #endif
 #endif
-#ifdef CONFIG_ATARI
-#include <asm/atarihw.h>
-#include <asm/atariints.h>
-#endif
 
 #include <linux/lp_m68k.h>
 #include <linux/lp_intern.h>
@@ -68,13 +68,16 @@
  */
 #define FORCE_POLLING	 0
 #define FORCE_INTERRUPT	 1
+/*
+ *  PREFER_INTERRUPT doesn't make much sense on m68k.
+ *  it is preserved here in case of joining with the i386 driver
+ *
 #define PREFER_INTERRUPT 2
+ */
 
 #define WHICH_DRIVER	FORCE_INTERRUPT
 
-#define MAX_LP 3 /* the maximum number of devices */
-
-struct lp_struct lp_table[MAX_LP] = {{0,},};
+struct lp_struct *lp_table[MAX_LP] = {NULL,};
 
 static int max_lp; /* the real number of devices */
 
@@ -100,9 +103,9 @@
 		count ++;
 		if(need_resched)
 			schedule();
-	} while (lp_table[dev].lp_is_busy(dev) && count < lp_table[dev].chars);
+	} while (lp_table[dev]->lp_is_busy(dev) && count < lp_table[dev]->chars);
 
-	if (count == lp_table[dev].chars) {
+	if (count == lp_table[dev]->chars) {
 		return 0;
 		/* we timed out, and the character was /not/ printed */
 	}
@@ -112,7 +115,7 @@
 		lp_max_count = count;
 	}
 #endif
-	lp_table[dev].lp_out(lpchar, dev);
+	lp_table[dev]->lp_out(lpchar, dev);
 	return 1;
 }
 #endif
@@ -127,8 +130,8 @@
 #if WHICH_DRIVER != FORCE_POLLING
 static __inline__ int lp_char_interrupt(char lpchar, int dev)
 {
-	if (!lp_table[dev].lp_is_busy(dev)) {
-		lp_table[dev].lp_out(lpchar,dev);
+	if (!lp_table[dev]->lp_is_busy(dev)) {
+		lp_table[dev]->lp_out(lpchar,dev);
 		return 1;
 	}
 	return 0;
@@ -136,37 +139,37 @@
 
 static int lp_error;
 
-static void lp_interrupt(int irq, struct pt_regs *fp, void *dummy)
+void lp_interrupt(int irq, void *dummy, struct pt_regs *fp)
 {
     unsigned long flags;
     int dev;
 
-    for (dev = 0; dev < max_lp; dev++) {
-	if (lp_table[dev].lp_my_interrupt(dev) != 0)
-	  if (lp_table[dev].do_print)
+    for (dev = 0; dev < MAX_LP; dev++) {
+	if ((lp_table[dev] != NULL) && (lp_table[dev]->lp_my_interrupt(dev) != 0))
+	  if (lp_table[dev]->do_print)
 	  {
-		  if (lp_table[dev].copy_size)
+		  if (lp_table[dev]->copy_size)
 		  {
 			  save_flags(flags);
 			  cli();
-			  if (lp_char_interrupt(lp_table[dev].lp_buffer[lp_table[dev].bytes_written], dev)) {
-				  --lp_table[dev].copy_size;
-				  ++lp_table[dev].bytes_written;
+			  if (lp_char_interrupt(lp_table[dev]->lp_buffer[lp_table[dev]->bytes_written], dev)) {
+				  --lp_table[dev]->copy_size;
+				  ++lp_table[dev]->bytes_written;
 				  restore_flags(flags);
 			  }
 			  else
 			  {
-				  lp_table[dev].do_print = 0;
+				  lp_table[dev]->do_print = 0;
 				  restore_flags(flags);
 				  lp_error = 1;
-				  wake_up_interruptible(&lp_table[dev].lp_wait_q);
+				  wake_up_interruptible(&lp_table[dev]->lp_wait_q);
 			  }
 		  }
 		  else
 		  {
-			  lp_table[dev].do_print = 0;
+			  lp_table[dev]->do_print = 0;
 			  lp_error = 0;
-			  wake_up_interruptible(&lp_table[dev].lp_wait_q);
+			  wake_up_interruptible(&lp_table[dev]->lp_wait_q);
 		  }
 
 	  }
@@ -187,21 +190,21 @@
   int dev = MINOR(inode->i_rdev);
 
   do {
-    lp_table[dev].do_print = 0;		/* disable lp_interrupt()   */
-    lp_table[dev].bytes_written = 0;	/* init buffer read-pointer */
+    lp_table[dev]->do_print = 0;		/* disable lp_interrupt()   */
+    lp_table[dev]->bytes_written = 0;	/* init buffer read-pointer */
     lp_error = 0;
-    lp_table[dev].copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
-    memcpy_fromfs(lp_table[dev].lp_buffer, buf, lp_table[dev].copy_size);
-    while (lp_table[dev].copy_size) {
+    lp_table[dev]->copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
+    memcpy_fromfs(lp_table[dev]->lp_buffer, buf, lp_table[dev]->copy_size);
+    while (lp_table[dev]->copy_size) {
       save_flags(flags);
       cli();				/* no interrupts now */
-      lp_table[dev].do_print = 1;	/* enable lp_interrupt() */
-      if (lp_char_interrupt(lp_table[dev].lp_buffer[lp_table[dev].bytes_written], dev)) {
-	++lp_table[dev].bytes_written;
-	--lp_table[dev].copy_size;
+      lp_table[dev]->do_print = 1;	/* enable lp_interrupt() */
+      if (lp_char_interrupt(lp_table[dev]->lp_buffer[lp_table[dev]->bytes_written], dev)) {
+	++lp_table[dev]->bytes_written;
+	--lp_table[dev]->copy_size;
 	lp_error = 0;
       } else {				/* something went wrong   */
-	lp_table[dev].do_print = 0;	/* disable lp_interrupt() */
+	lp_table[dev]->do_print = 0;	/* disable lp_interrupt() */
 	lp_error = 1;			/* printer caused error   */
       }
       if (lp_error) {
@@ -214,14 +217,14 @@
 	  current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
       }
   
-      interruptible_sleep_on(&lp_table[dev].lp_wait_q);
+      interruptible_sleep_on(&lp_table[dev]->lp_wait_q);
       restore_flags(flags);
   
       /* we're up again and running. we first disable lp_interrupt(), then
 	 check what happened meanwhile */
 
-      lp_table[dev].do_print = 0;
-      rc = total_bytes_written + lp_table[dev].bytes_written;
+      lp_table[dev]->do_print = 0;
+      rc = total_bytes_written + lp_table[dev]->bytes_written;
 
       if (current->signal & ~current->blocked) {
 	if (rc)
@@ -235,17 +238,17 @@
 	   figure out the type of error, exit on request or if nothing has 
 	   been printed at all. */
 	
-	if (lp_table[dev].lp_has_pout(dev)) {
+	if (lp_table[dev]->lp_has_pout(dev)) {
 	  printk(KERN_NOTICE "lp%d: paper-out\n",dev);
 	  if (!rc) rc = -ENOSPC;
-	} else if (!lp_table[dev].lp_is_online(dev)) {
+	} else if (!lp_table[dev]->lp_is_online(dev)) {
 	  printk(KERN_NOTICE "lp%d: off-line\n",dev);
 	  if (!rc) rc = -EIO;
-	} else if (lp_table[dev].lp_is_busy(dev)) {
+	} else if (lp_table[dev]->lp_is_busy(dev)) {
 	  printk(KERN_NOTICE "lp%d: on fire\n",dev);
 	  if (!rc) rc = -EIO;
 	}
-	if (lp_table[dev].flags & LP_ABORT)
+	if (lp_table[dev]->flags & LP_ABORT)
 	  return rc;
       }
       /* check if our buffer was completely printed, if not, most likely
@@ -253,15 +256,17 @@
 	 against, we start all over again. Else we set the read-pointer
 	 of the buffer and count the printed characters */
       
-      if (!lp_table[dev].copy_size) {
-	total_bytes_written += lp_table[dev].bytes_written;
-	buf += lp_table[dev].bytes_written;
-	count -= lp_table[dev].bytes_written;
+      if (!lp_table[dev]->copy_size) {
+	total_bytes_written += lp_table[dev]->bytes_written;
+	buf += lp_table[dev]->bytes_written;
+	count -= lp_table[dev]->bytes_written;
       }
     }
   } while (count > 0);
   return total_bytes_written;
 }
+#else
+void (*lp_interrupt)() = NULL;
 #endif
 
 #if WHICH_DRIVER != FORCE_INTERRUPT
@@ -277,7 +282,7 @@
 	int dev = MINOR(inode->i_rdev);
 
 #ifdef LP_DEBUG
-	if (jiffies-lp_last_call > lp_table[dev].time) {
+	if (jiffies-lp_last_call > lp_table[dev]->time) {
 		lp_total_chars = 0;
 		lp_max_count = 1;
 	}
@@ -293,25 +298,25 @@
 			lp_total_chars++;
 #endif
 		} else { /* if printer timed out */
-			if (lp_table[dev].lp_has_pout(dev)) {
+			if (lp_table[dev]->lp_has_pout(dev)) {
 				printk(KERN_NOTICE "lp%d: out of paper\n",dev);
-				if (lp_table[dev].flags & LP_ABORT)
+				if (lp_table[dev]->flags & LP_ABORT)
 					return temp - buf ? temp-buf : -ENOSPC;
 				current->state = TASK_INTERRUPTIBLE;
 				current->timeout = jiffies + LP_TIMEOUT_POLLED;
 				schedule();
-			} else if (!lp_table[dev].lp_is_online(dev)) {
+			} else if (!lp_table[dev]->lp_is_online(dev)) {
 				printk(KERN_NOTICE "lp%d: off-line\n",dev);
-				if (lp_table[dev].flags & LP_ABORT)
+				if (lp_table[dev]->flags & LP_ABORT)
 					return temp - buf ? temp-buf : -EIO;
 				current->state = TASK_INTERRUPTIBLE;
 				current->timeout = jiffies + LP_TIMEOUT_POLLED;
 				schedule();
 			} else
 	                /* not offline or out of paper. on fire? */
-			if (lp_table[dev].lp_is_busy(dev)) {
+			if (lp_table[dev]->lp_is_busy(dev)) {
 				printk(KERN_NOTICE "lp%d: on fire\n",dev);
-				if (lp_table[dev].flags & LP_ABORT)
+				if (lp_table[dev]->flags & LP_ABORT)
 					return temp - buf ? temp-buf : -EFAULT;
 				current->state = TASK_INTERRUPTIBLE;
 				current->timeout = jiffies + LP_TIMEOUT_POLLED;
@@ -327,11 +332,11 @@
 			}
 #ifdef LP_DEBUG
 			printk("lp sleeping at %d characters for %d jiffies\n",
-				lp_total_chars, lp_table[dev].time);
+				lp_total_chars, lp_table[dev]->time);
 			lp_total_chars = 0;
 #endif
 			current->state = TASK_INTERRUPTIBLE;
-			current->timeout = jiffies + lp_table[dev].time;
+			current->timeout = jiffies + lp_table[dev]->time;
 			schedule();
 		}
 	}
@@ -339,7 +344,7 @@
 }
 #endif
 
-static unsigned int lp_irq = 0;
+unsigned int lp_irq = 0;
 
 #if WHICH_DRIVER == PREFER_INTERRUPT
 static int lp_write(struct inode *inode, struct file *file,
@@ -362,21 +367,38 @@
 {
 	int dev = MINOR(inode->i_rdev);
 
-	if (dev >= max_lp)
+	if (dev >= MAX_LP)
+		return -ENODEV;
+#ifdef CONFIG_KERNELD
+	if (!lp_table[dev]) {
+		char modname[30];
+
+		sprintf(modname, "char-major-%d-%d", LP_MAJOR, dev);
+		request_module(modname);
+	}
+#endif
+	if (!lp_table[dev])
 		return -ENODEV;
-	if (!(lp_table[dev].flags & LP_EXIST))
+	if (!(lp_table[dev]->flags & LP_EXIST))
 		return -ENODEV;
-	if (lp_table[dev].flags & LP_BUSY)
+	if (lp_table[dev]->flags & LP_BUSY)
 		return -EBUSY;
 
-	lp_table[dev].flags |= LP_BUSY;
+	lp_table[dev]->flags |= LP_BUSY;
+
+	MOD_INC_USE_COUNT;
+	lp_table[dev]->lp_open();
 
 	return 0;
 }
 
 static void lp_release(struct inode *inode, struct file *file)
 {
-	lp_table[MINOR(inode->i_rdev)].flags &= ~LP_BUSY;
+	int dev =MINOR(inode->i_rdev);
+
+	lp_table[dev]->flags &= ~LP_BUSY;
+	lp_table[dev]->lp_release();
+	MOD_DEC_USE_COUNT;
 }
 
 
@@ -391,30 +413,33 @@
 #endif
 	if (minor >= max_lp)
 		return -ENODEV;
-	if (!(lp_table[minor].flags & LP_EXIST))
+	if (!(lp_table[minor]->flags & LP_EXIST))
 		return -ENODEV;
 	switch (cmd) {
 	case LPTIME:
-		lp_table[minor].time = arg;
+		lp_table[minor]->time = arg;
 		break;
 	case LPCHAR:
-		lp_table[minor].chars = arg;
+		lp_table[minor]->chars = arg;
 		break;
 	case LPABORT:
 		if (arg)
-			lp_table[minor].flags |= LP_ABORT;
+			lp_table[minor]->flags |= LP_ABORT;
 		else
-			lp_table[minor].flags &= ~LP_ABORT;
+			lp_table[minor]->flags &= ~LP_ABORT;
 		break;
 	case LPWAIT:
-		lp_table[minor].wait = arg;
+		lp_table[minor]->wait = arg;
 		break;
 	case LPSETIRQ:
 	case LPGETIRQ:
 	        retval = lp_irq;
 		break;
 	default:
-		retval = -EINVAL;
+		if (lp_table[minor]->lp_ioctl)
+			retval = lp_table[minor]->lp_ioctl(minor, cmd, arg);
+		else
+			retval = -EINVAL;
 	}
 	return retval;
 }
@@ -432,6 +457,16 @@
 	lp_release
 };
 
+static struct symbol_table parallel_syms = {
+#include <linux/symtab_begin.h>
+	X(lp_table),
+	X(lp_irq),
+	X(lp_interrupt),
+	X(lp_init),
+	X(register_parallel),
+	X(unregister_parallel),
+#include <linux/symtab_end.h>
+};
 
 int lp_init(void)
 {
@@ -442,7 +477,13 @@
 
 	if (register_chrdev(LP_MAJOR,"lp", &lp_fops)) {
 		printk("unable to get major %d for line printer\n", LP_MAJOR);
-		return -EBUSY;
+		return -ENXIO;
+	}
+
+	if (register_symtab(&parallel_syms)) {
+		unregister_chrdev(LP_MAJOR, "lp");
+		printk("unable to register parallel symtab\n");
+		return -ENXIO;
 	}
 
 #if WHICH_DRIVER == FORCE_POLLING
@@ -450,35 +491,14 @@
 	printk(KERN_INFO "lp_init: lp using polling driver\n");
 #else
 
-#ifdef CONFIG_AMIGA
-	if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_PARALLEL))
-		lp_irq = add_isr(IRQ_AMIGA_CIAA_FLG, lp_interrupt, 0,
-				 NULL, "printer");
-#endif
-#ifdef CONFIG_ATARI
-	if (MACH_IS_ATARI)
-		lp_irq = add_isr(IRQ_MFP_BUSY, lp_interrupt, IRQ_TYPE_SLOW,
-				 NULL, "printer");
+	lp_irq = 1;
+	printk(KERN_INFO "lp_init: lp using interrupt driver\n");
 #endif
 
-	if (lp_irq)
-		printk(KERN_INFO "lp_init: lp using interrupt\n");
-	else
-
-#if WHICH_DRIVER == PREFER_INTERRUPT
-		printk(KERN_INFO "lp_init: lp using polling driver\n");
-#else
-		printk(KERN_WARNING "lp_init: can't get interrupt, and polling driver not configured\n");
-#endif
-#endif
-
-	max_lp = 0;
-	max_lp += lp_internal_init(lp_table, max_lp, MAX_LP, WHICH_DRIVER);
+#ifndef MODULE
+	lp_internal_init();
 #ifdef CONFIG_MULTIFACE_III_LP
-	max_lp += lp_mfc_init(lp_table, max_lp, MAX_LP, WHICH_DRIVER);
-#if WHICH_DRIVER != FORCE_POLLING
-	add_isr(IRQ_AMIGA_PORTS, lp_interrupt, 0, NULL,
-		"Multiface III printer");
+	lp_mfc_init();
 #endif
 #endif
 	return 0;
@@ -489,4 +509,46 @@
  */
 void	lp_setup(char *str, int *ints)
 {	
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+return lp_init();
+}
+
+void cleanup_module(void)
+{
+unregister_chrdev(LP_MAJOR, "lp");
+}
+#endif
+
+/*
+ * (un-)register for hardware drivers
+ * tab is an inititalised lp_struct, dev the desired minor
+ * if dev < 0, let the driver choose the first free minor
+ * if sucessful return the minor, else -1
+ */
+int register_parallel(struct lp_struct *tab, int dev)
+{
+if (dev < 0) {
+	dev = 0;
+	while ((dev < MAX_LP) && (lp_table[dev] != NULL))
+		dev++;
+}
+if (dev > MAX_LP)
+	return -1;
+if (lp_table[dev] != NULL)
+	return -1;
+lp_table[dev] = tab;
+printk("lp%d: %s at 0x%08lx\n", dev, tab->name, (long)tab->base);
+return dev;
+}
+
+void unregister_parallel(int dev)
+{
+if ((dev < 0) || (dev > MAX_LP) || (lp_table[dev] == NULL))
+	printk("WARNING: unregister_parallel for non-existant device ignored!\n");
+else
+	lp_table[dev] = NULL;
 }
diff -urN pc-2.0.21/drivers/char/lp_mfc.c linux-2.0.21/drivers/char/lp_mfc.c
--- pc-2.0.21/drivers/char/lp_mfc.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/lp_mfc.c	Wed Sep 25 22:29:09 1996
@@ -0,0 +1,154 @@
+/*
+ * lp driver for the parallel port of a Multiface Card III
+ * 6.11.95 Joerg Dorchain (dorchain@mpi-sb.mpg.de)
+ *
+ * ported to 2.0.18 and modularised
+ * 25.9.96 Joerg Dorchain
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/lp_m68k.h>
+#include <linux/lp_mfc.h>
+#include <asm/amigahw.h>
+#include <asm/zorro.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include "multiface.h"
+#include "mc6821.h"
+
+static void lp_mfc_out(int,int);
+static int lp_mfc_busy(int);
+static int lp_mfc_pout(int);
+static int lp_mfc_online(int);
+static int lp_mfc_interrupt(int);
+
+static inline struct pia *pia(int);
+
+int lp_mfc_init(void);
+
+static volatile int dummy; /* for trigger reads */
+
+static void lp_mfc_out(int c, int dev)
+{
+int wait = 0;
+
+while (wait != lp_table[dev]->wait) wait++;
+dummy = pia(dev)->prb; /* trigger read clears irq bit*/
+pia(dev)->prb = c;  /* strobe goes down by hardware */
+}
+
+static int lp_mfc_busy(int dev)
+{
+return pia(dev)->pra&1;
+}
+
+static int lp_mfc_pout(int dev)
+{
+return pia(dev)->pra&2;
+}
+
+static int lp_mfc_online(int dev)
+{
+return pia(dev)->pra&4;
+}
+
+static int lp_mfc_interrupt(int dev)
+{
+int inter = pia(dev)->crb&128;
+
+dummy = pia(dev)->prb; /* clears irq bit */
+return inter;
+}
+
+static inline struct pia *pia(int dev)
+{
+return lp_table[dev]->base;
+}
+
+static void lp_mfc_open(void)
+{
+MOD_INC_USE_COUNT;
+}
+
+static void lp_mfc_release(void)
+{
+MOD_DEC_USE_COUNT;
+}
+
+static struct lp_struct tab[MAX_LP] = {{0,},};
+
+int lp_mfc_init()
+{
+int pias;
+struct pia *pp;
+int key;
+struct ConfigDev *cd;
+
+pias = 0;
+while((key = zorro_find( MANUF_BSC2, PROD_MULTIFACE_III, 0 , 0))) {
+  cd = zorro_get_board( key );
+  pp = (struct pia *)ZTWO_VADDR((((u_char *)cd->cd_BoardAddr)+PIABASE));
+  if (pias < MAX_LP) {
+	pp->crb = 0;
+	pp->ddrb = 255;    /* all pins output */
+	pp->crb = PIA_DDR|32|8;
+	dummy = pp->prb;
+	pp->crb |=(lp_irq!=0)?PIA_C1_ENABLE_IRQ:0; 
+	pp->cra = 0;
+	pp->ddra = 0xe0;   /* /RESET,  /DIR ,/AUTO-FEED output */
+	pp->cra = PIA_DDR;
+	pp->pra = 0;    /* reset printer */
+	udelay(5);
+	pp->pra |= 128;
+	tab[pias].name="Multiface III LP";
+	tab[pias].lp_out=lp_mfc_out;
+	tab[pias].lp_is_busy=lp_mfc_busy;
+	tab[pias].lp_has_pout=lp_mfc_pout;
+	tab[pias].lp_is_online=lp_mfc_online;
+	tab[pias].lp_my_interrupt=lp_mfc_interrupt;
+	tab[pias].lp_ioctl=NULL;
+	tab[pias].lp_open=lp_mfc_open;
+	tab[pias].lp_release=lp_mfc_release;
+	tab[pias].flags=LP_EXIST;
+	tab[pias].chars=LP_INIT_CHAR;
+	tab[pias].time=LP_INIT_TIME;
+	tab[pias].wait=LP_INIT_WAIT;
+	tab[pias].lp_wait_q=NULL;
+	tab[pias].base=pp;
+	tab[pias].type=LP_MFC;
+	if (register_parallel(tab + pias, -1) >=0) {
+	  zorro_config_board( key, 0 );
+	  pias++;
+	}
+	else
+	  printk("mfc_init: cant get a minor for pia at 0x%08lx\n",(long)pp);
+  }
+}
+if ((pias != 0) && (lp_irq != 0))
+  request_irq(IRQ_AMIGA_PORTS, lp_interrupt, 0,
+    "Multiface III printer", lp_mfc_init);
+
+return (pias==0)?-ENODEV:0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+return lp_mfc_init();
+}
+
+void cleanup_module(void)
+{
+int i;
+
+if (lp_irq)
+  free_irq(IRQ_AMIGA_PORTS, lp_mfc_init);
+for(i = 0; i < MAX_LP; i++)
+  if ((lp_table[i] != NULL) && (lp_table[i]->type == LP_MFC)) 
+    unregister_parallel(i);
+}
+#endif
diff -urN pc-2.0.21/drivers/char/m68kserial.c linux-2.0.21/drivers/char/m68kserial.c
--- pc-2.0.21/drivers/char/m68kserial.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/m68kserial.c	Tue Sep 17 21:28:26 1996
@@ -0,0 +1,1642 @@
+/*
+ *  linux/drivers/char/m68kserial.c
+ *
+ *
+ * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ *
+ * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
+ *
+ * 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.
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92.  Now
+ *  much more extensible to support other serial cards based on the
+ *  16450/16550A UART's.  Added support for the AST FourPort and the
+ *  Accent Async board.  
+ *
+ *  set_serial_info fixed to set the flags, custom divisor, and uart
+ * 	type fields.  Fix suggested by Michael K. Johnson 12/12/92.
+ *
+ * This module exports the following rs232 io functions:
+ *
+ *	long rs_init(long);
+ * 	int  rs_open(struct tty_struct * tty, struct file * filp)
+ */
+
+/*
+ * Notes and Design Goals:
+ * -----------------------
+ * The PC serial drivers can rely on the fact that all the serial
+ * hardware is very similar to program for all ports. Unfortunately,
+ * this is not true for m68k machines, especially the Atari. Here it is
+ * nearly the other way 'round: All ports need different treatment for
+ * the low-level stuff.
+ *
+ * For this reason, I've split the serial driver code into a
+ * port-independent part (serial.c) and port-specific parts (atari_*.c,
+ * ...). The first manages all what can be done without accessing the
+ * hardware directly, i.e. interfacing with the high-level tty drivers,
+ * wait queues, managing existing ports and the like. The latter do the
+ * actual hardware programming and are accessed by the hardware
+ * independent part by a "switch" structure, that contains pointers to
+ * functions for specific tasks. See the comment before the definition
+ * of the SERIALSWITCH structure in <linux/serial.h> for more details.
+ *
+ * The port-independent code should be usable by other machines than
+ * m68k ones, too, if there are similar circumstances with different
+ * serial port hardware. Feel free to use it, but please inform me if
+ * you have to do changes to it. I'll try to keep it really
+ * device-independent.
+ *
+ * Roman <Roman.Hodek@informatik.uni-erlangen.de>
+ */
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#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/ptrace.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+
+#ifdef CONFIG_ATARI
+#include "atari_SCC.h"
+#include "atari_MFPser.h"
+#include "atari_MIDI.h"
+#endif
+
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#ifdef CONFIG_AMIGA
+int amiga_serinit (void);
+
+#ifdef CONFIG_GVPIOEXT
+int ioext_init (void);
+#endif
+
+#ifdef CONFIG_MULTIFACE_III_TTY
+int multiface_init(void);
+#endif
+#endif
+
+DECLARE_TASK_QUEUE(tq_serial);
+
+struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL	1
+#define SERIAL_TYPE_CALLOUT	2
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+#define SERIAL_PARANOIA_CHECK
+#define CONFIG_SERIAL_NOPAUSE_IO
+#define SERIAL_DO_RESTART
+
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+
+#define _INLINE_ inline
+
+#define NR_PORTS	6
+
+struct async_struct rs_table[NR_PORTS];
+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
+
+static char *serialtypes[] = {
+	"unknown", "8250", "16450", "16550", "16550A"
+};
+#define PORT_MAX (sizeof(serialtypes)/sizeof(*serialtypes))
+#ifdef __mc68000__
+static char *serialtypes68k[] = {
+	"SCC w/o DMA", "SCC w/ DMA",
+	"MFP", "MFP w/o ctrl lines",
+	"MIDI",
+	"Amiga builtin", "GVP IO-Extender (16c552)", "BSC MultiFaceCard III"
+};
+#define M68K_PORT_MAX (sizeof(serialtypes68k)/sizeof(*serialtypes68k))
+#endif
+
+#ifndef MODULE
+#ifdef CONFIG_PROC_FS
+static int proc_serial_read (char *buffer, char **start, off_t offset,
+			     int size, int unused)
+{
+    int len, i;
+    char *name;
+
+    len = sprintf (buffer, "Serial ports:\n");
+    for (i = 0; i < NR_PORTS; ++i) {
+	if (!rs_table[i].port) continue;
+	if (rs_table[i].type >= 0 && rs_table[i].type < PORT_MAX)
+	    name = serialtypes[rs_table[i].type];
+#ifdef __mc68000__
+	else if (rs_table[i].type >= 100 &&
+		 rs_table[i].type < 100 + M68K_PORT_MAX)
+	    name = serialtypes68k[rs_table[i].type - 100];
+#endif
+	else
+	    name = "unknown";
+	len += sprintf (buffer + len, "\tttyS%d: %s at 0x%08x.\n", i, name,
+			rs_table[i].port);
+    }
+
+    if (len > 14)
+	return len;
+
+    /* No serial ports detected */
+    buffer[0] = 0;
+    return 0;
+}
+#endif
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the memcpy_fromfs 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 = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static inline int serial_paranoia_check(struct async_struct *info,
+					kdev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+	const char *badmagic =
+		"Warning: bad magic number for serial struct (%s) in %s\n";
+	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;
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+void rs_stop(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->device, "rs_stop"))
+		return;
+
+	info->sw->enab_tx_int( info, 0 );
+}
+
+void rs_start(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->device, "rs_start"))
+		return;
+
+	info->sw->enab_tx_int( info, 1 );
+}
+
+/*
+ * 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 (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);
+	}
+}
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred.  The path of
+ * hangup processing is:
+ *
+ * 	serial interrupt routine -> (scheduler tqueue) ->
+ * 	do_serial_hangup() -> tty->hangup() -> rs_hangup()
+ *
+ */
+static void do_serial_hangup(void *private_)
+{
+	struct async_struct	*info = (struct async_struct *) private_;
+	struct tty_struct	*tty;
+
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	tty_hangup(tty);
+}
+
+
+/*
+ * ---------------------------------------------------------------
+ * 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;
+	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);
+		restore_flags(flags);
+		return 0;
+	}
+
+	if (!info->port || !info->type) {
+		if (info->tty)
+			set_bit(TTY_IO_ERROR, &info->tty->flags);
+		free_page(page);
+		restore_flags(flags);
+		return 0;
+	}
+	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
+
+	/* initialize the hardware specific stuff for the port */
+	info->sw->init(info);
+
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+	/*
+	 * Set the speed and other port parameters.
+	 */
+	info->sw->change_speed(info);
+
+	info->flags |= ASYNC_INITIALIZED;
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * 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;
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
+#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);
+
+	/* do hardware specific deinitialization for the port */
+	info->sw->deinit( info, info->tty &&
+			 !(info->tty->termios->c_cflag & HUPCL) );
+
+	if (info->xmit_buf) {
+		free_page((unsigned long) info->xmit_buf);
+		info->xmit_buf = 0;
+	}
+
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags &= ~ASYNC_INITIALIZED;
+	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;
+
+	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;
+
+	info->sw->enab_tx_int( info, 1 );
+}
+
+static int rs_write(struct tty_struct * tty, int from_user,
+		    const unsigned char *buf, int count)
+{
+	int	c, total = 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;
+
+	if (from_user)
+		down(&tmp_buf_sem);
+	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) {
+			restore_flags(flags);
+			memcpy_fromfs(tmp_buf, buf, c);
+			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);
+		} 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 (from_user)
+		up(&tmp_buf_sem);
+	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped)
+		info->sw->enab_tx_int( info, 1 );
+	restore_flags(flags);
+	return total;
+}
+
+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);
+}
+
+/*
+ * ------------------------------------------------------------
+ * 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;
+#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)) {
+		info->x_char = STOP_CHAR(tty);
+		/* make sure the XOFF char is sent out if the TX was idle */
+		info->sw->enab_tx_int( info, 1 );
+	}
+
+	if (C_CRTSCTS(tty))
+		info->sw->throttle( info, 1 );
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+	unsigned long flags;
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+#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)) {
+		/* Protect against an tx int between the test and resetting x_char.
+		 * This would cause the XON to be lost. */
+		save_flags(flags);
+		cli();
+		if (info->x_char) {
+			info->x_char = 0;
+			restore_flags(flags);
+		}
+		else {
+			restore_flags(flags);
+			info->x_char = START_CHAR(tty);
+			/* make sure the XOFF char is sent out if the TX was idle */
+			info->sw->enab_tx_int( info, 1 );
+		}
+	}
+
+	if (C_CRTSCTS(tty))
+		info->sw->throttle( info, 0 );
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct async_struct * info,
+			   struct serial_struct * retinfo)
+{
+	struct serial_struct tmp;
+
+	if (!retinfo)
+		return -EFAULT;
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = info->type;
+	tmp.line = info->line;
+	tmp.port = 0;	/* meaningless for non-Intel */
+	tmp.irq  = 0;	/* meaningless for non-Intel */
+	tmp.flags = info->flags;
+	/* tmp.baud_base = info->baud_base;*/
+	tmp.close_delay = info->close_delay;
+	tmp.closing_wait = info->closing_wait;
+	tmp.custom_divisor = info->custom_divisor;
+	tmp.hub6 = 0;	/* meaningless for non-Intel */
+
+	/* At least baud_base and costum_divisor set by port-specific
+	 * function
+	 */
+	info->sw->get_serial_info( info, &tmp );
+
+	memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
+	return 0;
+}
+
+static int set_serial_info(struct async_struct * info,
+			   struct serial_struct * new_info)
+{
+	struct serial_struct new_serial;
+	struct async_struct old_info;
+	int retval = 0;
+
+	if (!new_info)
+		return -EFAULT;
+	memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));
+	old_info = *info;
+
+  	/* If a new custom divisor is to be set, let it check by the hardware
+ 	 * specific code first! (It is given the new info struct in case the
+ 	 * baud_base has also changed and valid divisors depend on the baud_base.)
+	 */
+	if (new_serial.custom_divisor != info->custom_divisor ||
+ 		new_serial.baud_base != info->baud_base) {
+		if (info->sw->check_custom_divisor( info, new_serial.baud_base,
+						   new_serial.custom_divisor ))
+			return( -EINVAL );
+	}
+
+	if (!suser()) {
+		if (/*(new_serial.baud_base != info->baud_base) ||*/
+		    (new_serial.type != info->type) ||
+		    (new_serial.close_delay != info->close_delay) ||
+		    ((new_serial.flags & ~ASYNC_USR_MASK) !=
+		     (info->flags & ~ASYNC_USR_MASK)))
+			return -EPERM;
+		info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+			       (new_serial.flags & ASYNC_USR_MASK));
+		info->custom_divisor = new_serial.custom_divisor;
+		info->baud_base = new_serial.baud_base;
+		goto check_and_exit;
+	}
+
+	/*
+	 * OK, past this point, all the error checking has been done.
+	 * At this point, we start making changes.....
+	 */
+
+	info->baud_base = new_serial.baud_base;
+	info->flags = ((info->flags & ~ASYNC_FLAGS) |
+			(new_serial.flags & ASYNC_FLAGS));
+	info->custom_divisor = new_serial.custom_divisor;
+	info->type = new_serial.type;
+	info->close_delay = new_serial.close_delay * HZ/100;
+	info->closing_wait = new_serial.closing_wait * HZ/100;
+
+check_and_exit:
+	if (!info->port || !info->type)
+		return 0;
+	if (info->flags & ASYNC_INITIALIZED) {
+		if (((old_info.flags & ASYNC_SPD_MASK) !=
+		     (info->flags & ASYNC_SPD_MASK)) ||
+		    (old_info.custom_divisor != info->custom_divisor) ||
+		    (old_info.baud_base != info->baud_base))
+			info->sw->change_speed(info);
+	} else
+		retval = startup(info);
+	return retval;
+}
+
+
+static int get_modem_info(struct async_struct * info, unsigned int *value)
+{
+	unsigned int result;
+
+	result = info->sw->get_modem_info (info);
+	put_user(result,value);
+	return 0;
+}
+
+static int set_modem_info(struct async_struct * info, unsigned int cmd,
+			  unsigned int *value)
+{
+	int error;
+	unsigned int arg;
+	int new_dtr = -1, new_rts = -1;
+
+	error = verify_area(VERIFY_READ, value, sizeof(int));
+	if (error)
+		return error;
+	arg = get_user(value);
+	switch (cmd) {
+	case TIOCMBIS:
+		if (arg & TIOCM_RTS)
+			new_rts = 1;
+		if (arg & TIOCM_DTR)
+			new_dtr = 1;
+		break;
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS)
+			new_rts = 0;
+		if (arg & TIOCM_DTR)
+			new_dtr = 0;
+		break;
+	case TIOCMSET:
+		new_dtr = !!(arg & TIOCM_DTR);
+		new_rts = !!(arg & TIOCM_RTS);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return( info->sw->set_modem_info( info, new_dtr, new_rts ) );
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break(	struct async_struct * info, int duration)
+{
+	unsigned long flags;
+
+	if (!info->port)
+		return;
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + duration;
+	save_flags(flags);
+	cli();
+	info->sw->set_break( info, 1 );
+	schedule();
+	info->sw->set_break( info, 0 );
+	restore_flags(flags);
+}
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	int error;
+	struct async_struct * info = (struct async_struct *)tty->driver_data;
+	int retval;
+	struct async_icount cprev, cnow;	/* kernel counter temps */
+	struct serial_icounter_struct *p_cuser;	/* user space */
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
+		return -ENODEV;
+
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
+	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
+	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		    return -EIO;
+	}
+
+	switch (cmd) {
+		case TCSBRK:	/* SVID version: non-zero arg --> no break */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			if (!arg)
+				send_break(info, HZ/4);	/* 1/4 second */
+			return 0;
+		case TCSBRKP:	/* support for POSIX tcsendbreak() */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			send_break(info, arg ? arg*(HZ/10) : HZ/4);
+			return 0;
+		case TIOCGSOFTCAR:
+			error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long));
+			if (error)
+				return error;
+			put_fs_long(C_CLOCAL(tty) ? 1 : 0,
+				    (unsigned long *) arg);
+			return 0;
+		case TIOCSSOFTCAR:
+			error = verify_area(VERIFY_READ, (void *) arg,
+					    sizeof(unsigned long));
+			if (error)
+				return error;
+			arg = get_fs_long((unsigned long *) arg);
+			tty->termios->c_cflag =
+				((tty->termios->c_cflag & ~CLOCAL) |
+				 (arg ? CLOCAL : 0));
+			return 0;
+		case TIOCMGET:
+			error = verify_area(VERIFY_WRITE, (void *) arg,
+				sizeof(unsigned int));
+			if (error)
+				return error;
+			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:
+			error = verify_area(VERIFY_WRITE, (void *) arg,
+						sizeof(struct serial_struct));
+			if (error)
+				return error;
+			return get_serial_info(info,
+					       (struct serial_struct *) arg);
+		case TIOCSSERIAL:
+			error = verify_area(VERIFY_READ, (void *) arg,
+					    sizeof(struct serial_struct));
+			if (error)
+				return error;
+			return set_serial_info(info,
+					       (struct serial_struct *) arg);
+		case TIOCSERCONFIG:
+			/* return do_autoconfig(info); */
+			return ( 0 );
+
+		case TIOCSERGWILD:
+			error = verify_area(VERIFY_WRITE, (void *) arg,
+					    sizeof(int));
+			if (error)
+				return error;
+			/* no wild mask */
+			put_fs_long(0, (unsigned long *) arg);
+			return 0;
+
+		case TIOCSERGETLSR: /* Get line status register */
+			error = verify_area(VERIFY_WRITE, (void *) arg,
+				sizeof(unsigned int));
+			if (error)
+				return error;
+			else {
+				put_fs_long(0,(unsigned long *) arg);
+				return 0;
+			}
+
+		case TIOCSERSWILD:
+			if (!suser())
+				return -EPERM;
+			return 0;
+
+		case TIOCSERGSTRUCT:
+			error = verify_area(VERIFY_WRITE, (void *) arg,
+						sizeof(struct async_struct));
+			if (error)
+				return error;
+			memcpy_tofs((struct async_struct *) arg,
+				    info, sizeof(struct async_struct));
+			return 0;
+
+		case TIOCSERGETMULTI:
+		case TIOCSERSETMULTI:
+			return -EINVAL;
+
+		/*
+		 * 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();
+			cprev = info->icount;	/* note the counters on entry */
+			restore_flags(flags);
+			while (1) {
+				interruptible_sleep_on(&info->delta_msr_wait);
+				/* see if a signal did it */
+				if (current->signal & ~current->blocked)
+					return -ERESTARTSYS;
+				save_flags(flags);
+				cli();
+				cnow = info->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:
+			error = verify_area(VERIFY_WRITE, (void *) arg,
+				sizeof(struct serial_icounter_struct));
+			if (error)
+				return error;
+			save_flags(flags);
+			cli();
+			cnow = info->icount;
+			restore_flags(flags);
+			p_cuser = (struct serial_icounter_struct *) arg;
+			put_user(cnow.cts, &p_cuser->cts);
+			put_user(cnow.dsr, &p_cuser->dsr);
+			put_user(cnow.rng, &p_cuser->rng);
+			put_user(cnow.dcd, &p_cuser->dcd);
+			return 0;
+
+		default:
+			if (info->sw->ioctl)
+				return( info->sw->ioctl( tty, file, info, cmd, arg ) );
+			else
+				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;
+
+	if (tty->termios->c_cflag == old_termios->c_cflag)
+		return;
+
+	info->sw->change_speed(info);
+
+	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;
+	unsigned long flags;
+	unsigned long timeout;
+
+	if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
+		return;
+
+	save_flags(flags); cli();
+
+	if (tty_hung_up_p(filp)) {
+		MOD_DEC_USE_COUNT;
+		restore_flags(flags);
+		return;
+	}
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_close ttys%d, count = %d\n", info->line, info->count);
+#endif
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  Info->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, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk("rs_close: bad serial port count for ttys%d: %d\n",
+		       info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+		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->normal_termios = *tty->termios;
+	if (info->flags & ASYNC_CALLOUT_ACTIVE)
+		info->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.
+	 */
+	if (info->flags & ASYNC_INITIALIZED) {
+		info->sw->stop_receive(info);
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important if there is a transmit FIFO!
+		 */
+		timeout = jiffies+HZ;
+		while (!(info->sw->trans_empty(info))) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + info->timeout;
+			schedule();
+			if (jiffies > timeout)
+				break;
+		}
+	}
+	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;
+			current->timeout = jiffies + info->close_delay;
+			schedule();
+		}
+		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_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void rs_hangup(struct tty_struct *tty)
+{
+	struct async_struct * info = (struct async_struct *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->device, "rs_hangup"))
+		return;
+
+	rs_flush_buffer(tty);
+	shutdown(info);
+	info->event = 0;
+	info->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)
+{
+	struct wait_queue wait = { current, NULL };
+	int		retval;
+	int		do_clocal = 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
+		if (info->flags & ASYNC_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -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 (info->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, info->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",
+	       info->line, info->count);
+#endif
+	save_flags(flags);
+	cli();
+	if (!tty_hung_up_p(filp))
+		info->count--;
+	restore_flags(flags);
+	info->blocked_open++;
+	while (1) {
+		save_flags(flags);
+		cli();
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE))
+			info->sw->set_modem_info( info, 1, 1 );
+		restore_flags(flags);
+		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 || (info->sw->get_modem_info( info ) & TIOCM_CAR)))
+			break;
+		if (current->signal & ~current->blocked) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+#ifdef SERIAL_DEBUG_OPEN
+		printk("block_til_ready blocking: ttys%d, count = %d\n",
+		       info->line, info->count);
+#endif
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		info->count++;
+	info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready after blocking: ttys%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	if (retval)
+		return retval;
+	info->flags |= ASYNC_NORMAL_ACTIVE;
+	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.
+ */
+int rs_open(struct tty_struct *tty, struct file * filp)
+{
+	struct async_struct	*info;
+	int 			retval, line;
+	unsigned long		page;
+
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	if ((line < 0) || (line >= NR_PORTS))
+		return -ENXIO;
+	info = rs_table + line;
+#ifdef CONFIG_KERNELD
+	if (!info->port) {
+		char modname[30];
+		sprintf(modname, "char-major-%d-%d", TTY_MAJOR, MINOR(tty->device));
+		request_module(modname);
+	}
+#endif
+	if (!info->port)
+		return -ENODEV;
+	if (serial_paranoia_check(info, tty->device, "rs_open"))
+		return -ENODEV;
+
+	if (info->sw->check_open) {
+		if ((retval = info->sw->check_open( info, tty, filp )))
+			return( retval );
+	}
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+	       info->count);
+#endif
+	info->count++;
+	tty->driver_data = info;
+	info->tty = tty;
+
+	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;
+	}
+
+	/*
+	 * Start up serial port
+	 */
+	retval = startup(info);
+	if (retval)
+		return retval;
+
+	MOD_INC_USE_COUNT;
+	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->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+			*tty->termios = info->normal_termios;
+		else
+			*tty->termios = info->callout_termios;
+		info->sw->change_speed(info);
+	}
+
+	info->session = current->session;
+	info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_open ttys%d successful...", info->line);
+#endif
+	return 0;
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * rs_init() and friends
+ *
+ * rs_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * Boot-time initialization code: Init the serial ports present on
+ * this machine type.
+ *
+ * Current HW Port to minor/device name mapping:
+ *
+ * Atari
+ *   TT                    | Falcon                    || minor | name
+ *  -----------------------+---------------------------++-------+-------
+ *   ST-MFP port (Modem1)  | (Modem1, internal only)   || 64    | ttyS0
+ *   SCC B (Modem2)        | SCC B (Modem2)            || 65    | ttyS1
+ *   TT-MFP port (Serial1) |                           || 66    | ttyS2
+ *   SCC A (Serial2)       |                           || 67    | ttyS3
+ *   SCC A (LAN)           | SCC A (LAN)               || 68    | ttyS4
+ *   Atari MIDI            | Atari MIDI                || 69    | ttyS5
+ *
+ * New 22/12/94: Midi is implemented as a serial device to make use of
+ * MIDI networks easier (SLIP on ttyS5). If there is need for MIDI
+ * specific high-level code, it can be implemented as a line
+ * discipline in future. ttyS4 is reserved to make space for the LAN
+ * device. It seems to be the better solution to split Serial2 and LAN
+ * into separate minors. And maybe the port to device mapping will
+ * change in future...
+ *
+ * 02/08/95: Now the mapping has changed! My old mapping scheme of the serial
+ * ports was a flaw in respect to order the devices following their
+ * capabilities. And it confused the users, to... The "new" order is the same
+ * as the one used by TOS. The only difference is that Serial2/LAN is split
+ * into two devices, only one of which can be open at one time. If one of
+ * these devices is opened, the respective port's hardware is activated via
+ * IO7 of the PSG (if connected...)
+ *
+ * Amiga
+ *   Amiga built-in serial port                        || 64    | ttyS0
+ *
+ * New 13/07/95: Support for multiple serial-boards of the same kind,
+ * and boards with dual (or higher) uarts. This is done by letting the
+ * drivers 'allocate' tty's in the rs_table[rs_count] array. A counter
+ * (rs_count) is therefore passed to all serial-init routines, that
+ * adds the amount of uarts detected, and returns the new value.
+ * I think this is the best way to manage the various serial-boards
+ * available (atleast for the Amiga), and should cause no harm to the
+ * old drivers.                         - Jes Sorensen (jds@kom.auc.dk)
+ *
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static void show_serial_version(void)
+{
+	printk(KERN_INFO "M68K Serial driver version 1.01\n");
+}
+
+static struct symbol_table serial_syms = {
+#include <linux/symtab_begin.h>
+	X(rs_table),
+	X(tq_serial),
+	X(register_serial),
+	X(unregister_serial),
+	X(rs_start),
+	X(rs_stop),
+	X(rs_init),
+#include <linux/symtab_end.h>
+};
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+int rs_init(void)
+{
+	int i, rs_count;
+	struct async_struct * info;
+
+	rs_count = 0; /* All machines start at rs_table[0] (minor 64). */
+
+	init_bh(SERIAL_BH, do_serial_bh);
+
+	show_serial_version();
+
+	/* Initialize the tty_driver structure */
+
+	memset(&serial_driver, 0, sizeof(struct tty_driver));
+	serial_driver.magic = TTY_DRIVER_MAGIC;
+	serial_driver.name = "ttyS";
+	serial_driver.major = TTY_MAJOR;
+	serial_driver.minor_start = 64;
+	serial_driver.num = NR_PORTS;
+	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;
+
+	/*
+	 * 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;
+
+	if (tty_register_driver(&serial_driver)) {
+		printk("Couldn't register serial driver\n");
+		return -EIO;
+	}
+	if (tty_register_driver(&callout_driver)) {
+		tty_unregister_driver(&serial_driver);
+		printk("Couldn't register callout driver\n");
+		return -EIO;
+	}
+
+	if (register_symtab(&serial_syms) != 0) {
+		tty_unregister_driver(&serial_driver);
+		tty_unregister_driver(&callout_driver);
+		printk("Couldn't register serial symtab\n");
+		return -EIO;
+	}
+
+	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
+		info->magic = SERIAL_MAGIC;
+		info->line = i;
+		info->port = 0;
+		info->tty = 0;
+		info->type = PORT_UNKNOWN;
+		info->custom_divisor = 0;
+		info->close_delay = 5*HZ/10;
+		info->closing_wait = 30*HZ;
+		info->x_char = 0;
+		info->event = 0;
+		info->count = 0;
+		info->blocked_open = 0;
+		info->tqueue.routine = do_softint;
+		info->tqueue.data = info;
+		info->tqueue_hangup.routine = do_serial_hangup;
+		info->tqueue_hangup.data = info;
+		info->callout_termios =callout_driver.init_termios;
+		info->normal_termios = serial_driver.init_termios;
+		info->open_wait = 0;
+		info->close_wait = 0;
+		info->delta_msr_wait = 0;
+		info->icount.cts = info->icount.dsr = 0;
+		info->icount.rng = info->icount.dcd = 0;
+		info->next_port = 0;
+		info->prev_port = 0;
+	}
+
+#ifndef MODULE
+#ifdef CONFIG_PROC_FS
+	proc_register_dynamic(&proc_root, &(struct proc_dir_entry) {
+		0, 6, "serial",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, NULL /* ops -- default to array */,
+		&proc_serial_read /* get_info */,
+	});
+#endif /* PROC_FS */
+	switch (boot_info.machtype) {
+	    case MACH_ATARI:
+#ifdef CONFIG_ATARI_SCC
+	    atari_SCC_init();
+#endif
+#ifdef CONFIG_ATARI_MFPSER
+	    atari_MFPser_init();
+#endif
+#ifdef CONFIG_ATARI_MIDI
+	    atari_MIDI_init();
+#endif
+	    break;
+
+	    case MACH_AMIGA:
+#ifdef CONFIG_AMIGA_BUILTIN_SERIAL
+	    amiga_serinit();
+#endif
+#ifdef CONFIG_GVPIOEXT
+	    ioext_init();
+#endif
+#ifdef CONFIG_MULTIFACE_III_TTY
+	    multiface_init();
+#endif
+	    break;
+	} /* end switch on machine type */
+#endif
+
+	return 0;
+}
+
+/*
+ * register_serial and unregister_serial allows for serial ports to be
+ * configured at run-time, to support PCMCIA modems.
+ *
+ * ++roman: Removed PCish stuff from here... New calling interface: The caller
+ * can set req->line to a value >= 0 to request a particular slot. If
+ * req->line is < 0, the first free slot is assigned. Other fields that must
+ * be filled in before the call are 'port' and 'type'.
+ */
+int register_serial(struct serial_struct *req)
+{
+	int i;
+	unsigned long flags;
+	struct async_struct *info;
+
+	save_flags(flags);
+	cli();
+#ifndef __mc68000__
+	for (i = 0; i < NR_PORTS; i++) {
+		if (rs_table[i].port == req->port)
+			break;
+	}
+	if (i == NR_PORTS) {
+#else
+	if (req->line >= 0) {
+		if (req->line >= NR_PORTS)
+		    return -EINVAL;
+		if (rs_table[req->line].type != PORT_UNKNOWN ||
+		    rs_table[req->line].count > 0) {
+		    /* already allocated */
+		    restore_flags(flags);
+		    return -EBUSY;
+		}
+		i = req->line;
+	}
+	else {
+#endif
+		for (i = 0; i < NR_PORTS; i++)
+			if ((rs_table[i].type == PORT_UNKNOWN) &&
+			    (rs_table[i].count == 0))
+				break;
+	}
+	if (i == NR_PORTS) {
+		restore_flags(flags);
+		return -EAGAIN;
+	}
+	info = &rs_table[i];
+	if (rs_table[i].count) {
+		restore_flags(flags);
+		printk("Couldn't configure serial #%d (port=%d,irq=%d): "
+		       "device already open\n", i, req->port, req->irq);
+		return -EBUSY;
+	}
+#ifndef __mc68000__
+	info->irq = req->irq;
+	info->port = req->port;
+	autoconfig(info);
+	if (info->type == PORT_UNKNOWN) {
+		restore_flags(flags);
+		printk("register_serial(): autoconfig failed\n");
+		return -1;
+	}
+	printk(KERN_INFO "tty%02d at 0x%04x (irq = %d)", info->line,
+	       info->port, info->irq);
+#else
+	info->port = req->port;
+	info->type = req->type;
+	printk(KERN_INFO "ttyS%d at 0x%08x: ", info->line, info->port);
+#endif
+	if (info->type >= 0 && info->type < PORT_MAX)
+	    printk( "%s\n", serialtypes[info->type] );
+#ifdef __mc68000__
+	else if (info->type >= 100 && info->type < 100 + M68K_PORT_MAX)
+	    printk( "%s\n", serialtypes68k[info->type - 100] );
+#endif
+	else
+	    printk( "\n" );
+	restore_flags(flags);
+	return info->line;
+}
+
+void unregister_serial(int line)
+{
+	unsigned long flags;
+	struct async_struct *info = &rs_table[line];
+
+	save_flags(flags);
+	cli();
+	if (info->tty)
+		tty_hangup(info->tty);
+	info->type = PORT_UNKNOWN;
+#ifdef __mc68000__
+	info->port = 0;
+#endif
+	printk(KERN_INFO "tty%02d unloaded\n", info->line);
+	restore_flags(flags);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return rs_init();
+}
+
+void cleanup_module(void)
+{
+	if (tty_unregister_driver(&serial_driver))
+		printk("SERIAL: failed to unregister serial driver\n");
+	if (tty_unregister_driver(&callout_driver))
+		printk("SERIAL: failed to unregister callout driver\n");
+}
+#endif /* MODULE */
diff -urN pc-2.0.21/drivers/char/mc6821.h linux-2.0.21/drivers/char/mc6821.h
--- pc-2.0.21/drivers/char/mc6821.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/mc6821.h	Wed Sep 25 22:28:37 1996
@@ -0,0 +1,51 @@
+#ifndef _MC6821_H_
+#define _MC6821_H_
+
+/*
+ * This file describes the memery mapping of the MC6821 PIA.
+ * The unions describe overlayed registers. Which of them is used is
+ * determined by bit 2 of the corresponding control register.
+ * this files expects the PIA_REG_PADWIDTH to be defined the numeric
+ * value of the register spacing.
+ *
+ * Data came from MFC-31-Developer Kit (from Ralph Seidel,
+ * zodiac@darkness.gun.de) and Motorola Data Sheet (from 
+ * Richard Hirst, srh@gpt.co.uk)
+ *
+ * 6.11.95 copyright Joerg Dorchain (dorchain@mpi-sb.mpg.de)
+ *
+ */
+
+#ifndef PIA_REG_PADWIDTH
+#define PIA_REG_PADWIDTH 255
+#endif
+
+struct pia {
+union {
+volatile u_char pra;
+volatile u_char ddra;
+} ua;
+u_char pad1[PIA_REG_PADWIDTH];
+volatile u_char cra;
+u_char pad2[PIA_REG_PADWIDTH];
+union {
+volatile u_char prb;
+volatile u_char ddrb;
+} ub;
+u_char pad3[PIA_REG_PADWIDTH];
+volatile u_char crb;
+u_char pad4[PIA_REG_PADWIDTH];
+};
+
+#define pra ua.pra
+#define ddra ua.ddra
+#define prb ub.prb
+#define ddrb ub.ddrb
+
+#define PIA_C1_ENABLE_IRQ (1<<0)
+#define PIA_C1_LOW_TO_HIGH (1<<1)
+#define PIA_DDR (1<<2)
+#define PIA_IRQ2 (1<<6)
+#define PIA_IRQ1 (1<<7)
+
+#endif
diff -urN pc-2.0.21/drivers/char/mc68681.h linux-2.0.21/drivers/char/mc68681.h
--- pc-2.0.21/drivers/char/mc68681.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/mc68681.h	Sat Aug 24 14:34:47 1996
@@ -0,0 +1,131 @@
+#ifndef _MC68681_H_
+#define _MC68681_H_
+
+/* 
+ * This describes an MC68681 DUART. It has almost only overlayed registers, which
+ * the structure very ugly.
+ * Note that the ri-register isn't really a register of the duart but a kludge of bsc
+ * to make the ring indicator available.
+ * 
+ * The data came from the MFC-31-Developer Kit (from Ralph Seidel,
+ * zodiac@darkness.gun.de) and the data sheet of Phillip's clone device (SCN68681)
+ * (from Richard Hirst, srh@gpt.co.uk)
+ *
+ * 11.11.95 copyright Joerg Dorchain (dorchain@mpi-sb.mpg.de)
+ *
+ */
+
+struct duarthalf {
+union {
+volatile u_char mr1; /* rw */
+volatile u_char mr2; /* rw */
+}  mr;
+volatile u_char ri;   /* special, read */
+union {
+volatile u_char sr;  /* read */
+volatile u_char csr; /* write */
+} sr_csr;
+u_char pad1;
+volatile u_char cr; /* write */
+u_char pad2;
+union {
+volatile u_char rhr; /* read */
+volatile u_char thr; /* write */
+} hr;
+u_char pad3;
+};
+
+struct duart {
+struct duarthalf pa;
+union {
+volatile u_char ipcr; /* read */
+volatile u_char acr;  /* write */
+} ipcr_acr;
+u_char pad1;
+union {
+volatile u_char isr; /* read */
+volatile u_char imr; /* write */
+} ir;
+u_char pad2;
+volatile u_char ctu;
+u_char pad3;
+volatile u_char ctl;
+u_char pad4;
+struct duarthalf pb;
+volatile u_char ivr;
+u_char pad5;
+union {
+volatile u_char ipr; /* read */
+volatile u_char opcr; /* write */
+} ipr_opcr;
+u_char pad6;
+union {
+volatile u_char start; /* read */
+volatile u_char sopc; /* write */
+} start_sopc;
+u_char pad7;
+union {
+volatile u_char stop; /* read */
+volatile u_char ropc; /* write */
+} stop_ropc;
+u_char pad8;
+};
+
+#define MR1_BITS 3
+#define MR1_5BITS 0
+#define MR1_6BITS 1
+#define MR1_7BITS 2
+#define MR1_8BITS 3
+
+#define MR1_PARITY_ODD 4
+
+#define MR1_PARITY 24
+#define MR1_PARITY_WITH 0
+#define MR1_PARITY_FORCE 8
+#define MR1_PARITY_NO 16
+#define MR1_PARITY_MULTIDROP 24
+
+#define MR1_ERROR_BLOCK 32
+#define MR1_FFULL_IRQ 64
+#define MR1_RxRTS_ON 128
+
+#define MR2_STOPS 15
+#define MR2_1STOP 7
+#define MR2_2STOP 15
+
+#define MR2_CTS_ON 16
+#define MR2_TxRTS_ON 32
+
+#define MR2_MODE 192
+#define MR2_NORMAL 0
+#define MR2_ECHO 64
+#define MR2_LOCALLOOP 128
+#define MR2_REMOTELOOP 192
+
+#define CR_RXCOMMAND 3
+#define CR_NONE 0
+#define CR_RX_ON 1
+#define CR_RX_OFF 2
+#define CR_TXCOMMAND 12
+#define CR_TX_ON 4
+#define CR_TX_OFF 8
+#define CR_MISC 112
+#define CR_RESET_MR 16
+#define CR_RESET_RX 32
+#define CR_RESET_TX 48
+#define CR_RESET_ERR 64
+#define CR_RESET_BREAK 80
+#define CR_START_BREAK 96
+#define CR_STOP_BREAK 112
+
+#define SR_RXRDY 1
+#define SR_FFULL 2
+#define SR_TXRDY 4
+#define SR_TXEMPT 8
+#define SR_OVERRUN 16
+#define SR_PARITY 32
+#define SR_FRAMING 64
+#define SR_BREAK 128
+
+
+#endif
diff -urN pc-2.0.21/drivers/char/mem.c linux-2.0.21/drivers/char/mem.c
--- pc-2.0.21/drivers/char/mem.c	Fri Sep 20 12:17:18 1996
+++ linux-2.0.21/drivers/char/mem.c	Thu Sep 26 22:03:24 1996
@@ -23,7 +23,7 @@
 #include <asm/io.h>
 #include <asm/pgtable.h>
 
-#ifdef CONFIG_SOUND
+#if defined (CONFIG_SOUND)
 void soundcard_init(void);
 #endif
 #ifdef CONFIG_ISDN
@@ -33,6 +33,13 @@
 void pcwatchdog_init(void);
 #endif
 
+#if defined(__mc68000__)
+extern void fbmem_init( void );
+#endif
+#if defined(CONFIG_AMIGA_GSP)
+extern void gsp_init(void);
+#endif
+
 static int read_ram(struct inode * inode, struct file * file, char * buf, int count)
 {
 	return -EIO;
@@ -56,7 +63,8 @@
 	if (count > high_memory - p)
 		count = high_memory - p;
 	read = 0;
-#if defined(__i386__) || defined(__sparc__) /* we don't have page 0 mapped on x86/sparc.. */
+#if defined(__i386__) || defined(__sparc__) || defined(__mc68000__)
+	/* we don't have page 0 mapped on x86, sparc and m68k.. */
 	while (p < PAGE_OFFSET + PAGE_SIZE && count > 0) {
 		put_user(0,buf);
 		buf++;
@@ -84,7 +92,8 @@
 	if (count > high_memory - p)
 		count = high_memory - p;
 	written = 0;
-#if defined(__i386__) || defined(__sparc__) /* we don't have page 0 mapped on x86/sparc.. */
+#if defined(__i386__) || defined(__sparc__) || defined(__mc68000__)
+	/* we don't have page 0 mapped on x86, sparc and m68k.. */
 	while (PAGE_OFFSET + p < PAGE_SIZE && count > 0) {
 		/* Hmm. Do something? */
 		buf++;
@@ -136,6 +145,9 @@
 
 static int read_port(struct inode * inode, struct file * file,char * buf, int count)
 {
+#if defined(__mc68000__)
+  return -EIO;
+#else
 	unsigned int i = file->f_pos;
 	char * tmp = buf;
 
@@ -146,10 +158,14 @@
 	}
 	file->f_pos = i;
 	return tmp-buf;
+#endif
 }
 
 static int write_port(struct inode * inode, struct file * file, const char * buf, int count)
 {
+#if defined(__mc68000__)
+  return -EIO;
+#else
 	unsigned int i = file->f_pos;
 	const char * tmp = buf;
 
@@ -160,6 +176,7 @@
 	}
 	file->f_pos = i;
 	return tmp-buf;
+#endif
 }
 
 static int read_null(struct inode * node, struct file * file, char * buf, int count)
@@ -389,6 +406,12 @@
 	if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
 		printk("unable to get major %d for memory devs\n", MEM_MAJOR);
 	rand_initialize();
+#if defined (__mc68000__)
+	fbmem_init();
+#endif
+#if defined (CONFIG_AMIGA_GSP)
+	gsp_init();
+#endif
 	tty_init();
 #ifdef CONFIG_PRINTER
 	lp_init();
@@ -396,11 +419,12 @@
 #if defined (CONFIG_BUSMOUSE) || defined(CONFIG_UMISC) || \
     defined (CONFIG_PSMOUSE) || defined (CONFIG_MS_BUSMOUSE) || \
     defined (CONFIG_ATIXL_BUSMOUSE) || defined(CONFIG_SOFT_WATCHDOG) || \
+    defined (CONFIG_AMIGAMOUSE) || defined (CONFIG_ATARIMOUSE) || \
     defined (CONFIG_PCWATCHDOG) || \
     defined (CONFIG_APM) || defined (CONFIG_RTC) || defined (CONFIG_SUN_MOUSE)
 	misc_init();
 #endif
-#ifdef CONFIG_SOUND
+#if defined (CONFIG_SOUND)
 	soundcard_init();
 #endif
 #if CONFIG_QIC02_TAPE
diff -urN pc-2.0.21/drivers/char/misc.c linux-2.0.21/drivers/char/misc.c
--- pc-2.0.21/drivers/char/misc.c	Thu Aug 22 17:45:56 1996
+++ linux-2.0.21/drivers/char/misc.c	Mon Aug 26 23:56:55 1996
@@ -65,6 +65,8 @@
 extern int psaux_init(void);
 extern int ms_bus_mouse_init(void);
 extern int atixl_busmouse_init(void);
+extern int amiga_mouse_init(void);
+extern int atari_mouse_init(void);
 extern int sun_mouse_init(void);
 extern void watchdog_init(void);
 extern void pcwatchdog_init(void);
diff -urN pc-2.0.21/drivers/char/multiface.h linux-2.0.21/drivers/char/multiface.h
--- pc-2.0.21/drivers/char/multiface.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/multiface.h	Sat Aug 24 14:34:51 1996
@@ -0,0 +1,20 @@
+#ifndef _MULTIFACE_H_
+#define _MULTIFACE_H_
+
+/*
+ * Defines for SerialMaster, Multiface Card II and Multiface Card III
+ * The addresses given below are offsets to the board base address
+ * 
+ * 6.11.95 Joerg Dorchain (dorchain@mpi-sb.mpg.de)
+ *
+ */
+
+#define PIA_REG_PADWIDTH 255
+
+#define DUARTBASE 0x0000
+#define PITBASE   0x0100
+#define ROMBASE   0x0200
+#define PIABASE   0x4000
+
+#endif
+
diff -urN pc-2.0.21/drivers/char/selection.c linux-2.0.21/drivers/char/selection.c
--- pc-2.0.21/drivers/char/selection.c	Thu Jun  6 12:42:16 1996
+++ linux-2.0.21/drivers/char/selection.c	Sat Aug 24 14:35:01 1996
@@ -119,6 +119,12 @@
 
 	do_unblank_screen();
 
+#if defined(__mc68000__)
+	video_num_lines = get_video_num_lines(fg_console);
+	video_num_columns = get_video_num_columns(fg_console);
+	video_size_row = get_video_size_row(fg_console);
+#endif
+
 	{ unsigned short *args, xs, ys, xe, ye;
 
 	  args = (unsigned short *)(arg + 1);
diff -urN pc-2.0.21/drivers/char/selection.h linux-2.0.21/drivers/char/selection.h
--- pc-2.0.21/drivers/char/selection.h	Fri Sep 20 14:22:06 1996
+++ linux-2.0.21/drivers/char/selection.h	Thu Sep 26 22:51:08 1996
@@ -12,6 +12,11 @@
 extern int mouse_reporting(void);
 extern void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry);
 
+#if defined(__mc68000__)
+extern unsigned long get_video_num_columns(unsigned int console);
+extern unsigned long get_video_num_lines(unsigned int console);
+extern unsigned long get_video_size_row(unsigned int console);
+#endif
 extern unsigned long video_num_columns;
 extern unsigned long video_num_lines;
 extern unsigned long video_size_row;
diff -urN pc-2.0.21/drivers/char/ser_ioext.c linux-2.0.21/drivers/char/ser_ioext.c
--- pc-2.0.21/drivers/char/ser_ioext.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/ser_ioext.c	Sat Aug 24 14:35:03 1996
@@ -0,0 +1,889 @@
+/*
+ * This code is (C) 1995 Jes Sorensen (jds@kom.auc.dk).
+ *
+ * This is a driver for the GVP IO-Extender (duart, midi, parallel
+ * board) for the Amiga (so far only the serial part is implemented).
+ *
+ * The code is partly based on Roman's Atari drivers, Hamish'
+ * driver the internal Amiga port and the PC drivers.
+ *
+ * The board info was kindly supplied by Erick Quackenbush, former GVP
+ * employee - thanks. The missing info on about the "secret" register
+ * was obtained from the GVP SCSI driver though.
+ *
+ * Thanks to Niels Pontoppidan (neil@dannug.dk), for letting me borrow
+ * his IO-Extender, and thus making it possible for me to do this
+ * driver. Also thanks to Ralph Babel and Roman Hodek for a few hints
+ * and to Michael Goodwin for doing all the beta-testing - especially
+ * after I returned the board I borrowed from Niels.
+ *
+ * If you can use this code (or parts of it) - feel free to copy it,
+ * but please let me know what parts you can use and where you use it.
+ *
+ * As the code probably isn't bug-free yet, I would also like to know
+ * if you discover any bugs.
+ *
+ * Facts: 
+ *
+ * The tty-assignment is done dynamically so only the needed number of
+ * tty's is allocated, leaving room for other serial boards in the
+ * system. This needed some changes in serial.c aswell as the other
+ * drivers, but I do find this the best way to handle things - I hate
+ * static allocation!
+ *
+ * Note: To use the serial ports at rates higher than 38400, you can
+ * compile the driver with IOEXT_FAST_RATES = 1, and then select
+ * one of these rates:
+ *
+ * 300 = 57600, 1200 = 115200
+ *
+ * The driver should be able to handle multiple IO-Extenders, but I
+ * haven't been able to test this as I only got access to one board at
+ * the moment. The number of extenders is limited to five (same as the
+ * maximum number of Zorro slots) but if you wan't to use more than
+ * two you should change "NR_PORTS" in serial.h to fit your needs.
+ *
+ * 07/13/95: Initial version - the code is definately _not_ perfect
+ *           yet - it doesn't even work :-(
+ * 10/01/95: First version, it seems to work pretty well, even at
+ *           serial-rates higher than 19200.
+ *           There is still one major problem, as I get lotsa overruns
+ *           when the harddisk is accessed during receives. This is
+ *           probably caused by the SCSI-driver running too long with
+ *           interrupts off, so I'm not sure there is anything I can
+ *           do about it in here.
+ * 10/04/95: Added support for higher (57k6 and 115k2) baud-rates when
+ *           the ASYNC_SPD_{HI,VHI} flags are set.
+ * 95/10/19: Adapted to 1.2 (Andreas Schwab).
+ *	     Compiles, but otherwise untestet!
+ * 95/11/26: Updated for 1.2.13pl3 - updated the GVP detection aswell.
+ * 96/01/17: Hopefully solved the GVP Zorro detection problem in
+ *           1.2.13pl4 that occoured when a user had several GVP
+ *           boards installed. (Jes)
+ * 96/02/07: The driver actually decreases info->xmit_cnt-- now
+ *           whenever a character is transmitted, so we don't just
+ *           transmit the same char now. This is the reason why this
+ *           driver hasn't worked under 1.2 so far. Thanks to Murray
+ *           Bennett for this, found it in his patches.
+ * 96/02/27: Integrate various changes to make it work. (murray)
+ *           No need to protect against interrupt reentrance.
+ *           No need to acknowledge the interrupt (it is done for us).
+ *           Process as many conditions as possible during the interrupt.
+ *           Use the 16 byte transmit fifo.
+ *           Handle THRE interrupts properly. This is a level driven interrupt,
+ *           not an edge triggered interrupt, so only enable it when we have
+ *           characters to send, and disable it as soon as we have sent the
+ *           last character.
+ *           Don't try to clear pending interrupts during ioext_init() since
+ *           reading IIR achieves nothing.
+ *           Register a separate interrupt handler per board. For some
+ *           strange reason, I couldn't get it to work with a single handler.
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+
+#include <asm/setup.h>
+#include <asm/irq.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/zorro.h>
+#include <asm/amigatypes.h>
+
+#include "ser_ioext.h"
+
+#define IOEXT_DEBUG 0 /* Remove this when the driver is finished */
+#undef DEBUG_IRQ
+#define DEBUG_IRQ 0 /* Remove this when the driver is finished */
+#undef DEBUG_IRQ
+#define DEBUG 0 /* Remove this when the driver is finished */
+#undef DEBUG
+
+#define FIFO_SIZE 16  /* Size of hardware send and receive FIFOs */
+
+#define IOEXT_FAST_RATES 1   /*
+			      * When this is set, the rates 110 and
+			      * 134 will run 57600 and 115200
+			      * respectively.
+			      */
+
+#define FIFO_TRIGGER_LEVEL FIFO_TRIG_8
+                             /*
+			      * There are 4 receiver FIFO-interrupt *
+			      * trigger levels (FIFO_TRIG_x), that  *
+			      * indicates how many bytes are to be  *
+			      * allowed in the receiver-FIFO before *
+			      * an interrupt is generated:          *
+			      *                x =  1 =  1 byte	    *
+			      *                x =  4 =  4 bytes    *
+			      *                x =  8 =  8 bytes    *
+			      *                x = 14 = 14 bytes    *
+			      * 14 works for me, but if you keep    *
+			      * getting overruns try lowering this  *
+			      * value one step at a time.           *
+			      */
+
+/***************************** Prototypes *****************************/
+static void ser_init( struct async_struct *info );
+static void ser_deinit( struct async_struct *info, int leave_dtr );
+static void ser_enab_tx_int( struct async_struct *info, int enab_flag );
+static int  ser_check_custom_divisor(struct async_struct *info,
+				     int baud_base, int divisor);
+static void ser_change_speed( struct async_struct *info );
+static void ser_throttle( struct async_struct *info, int status );
+static void ser_set_break( struct async_struct *info, int break_flag );
+static void ser_get_serial_info( struct async_struct *info,
+				struct serial_struct *retinfo );
+static unsigned int ser_get_modem_info( struct async_struct *info );
+static int ser_set_modem_info( struct async_struct *info, int new_dtr,
+			      int new_rts );
+static void ser_stop_receive(struct async_struct *info);
+static int ser_trans_empty(struct async_struct *info);
+/************************* End of Prototypes **************************/
+
+/*
+ * SERIALSWITCH structure for the GVP IO-Extender serial-board.
+ */
+
+static SERIALSWITCH ioext_ser_switch = {
+    ser_init,
+    ser_deinit,
+    ser_enab_tx_int,
+    ser_check_custom_divisor,
+    ser_change_speed,
+    ser_throttle,
+    ser_set_break,
+    ser_get_serial_info,
+    ser_get_modem_info,
+    ser_set_modem_info,
+    NULL,
+    ser_stop_receive, ser_trans_empty, NULL
+};
+
+static int ioext_baud_table[18] = {
+ 	/* B0     */ 0, /* Newer use this value !!! */
+	/* B50    */ 9216,
+	/* B75    */ 6144,
+	/* B110   */ 4189, /* There's a little rounding on this one */
+	/* B134	  */ 3439, /* Same here! */
+	/* B150	  */ 3072,
+	/* B200	  */ 2304,
+#if IOEXT_FAST_RATES
+	/* B57600 */ 8,
+#else
+	/* B300	  */ 1536,
+#endif
+	/* B600	  */ 768,
+#if IOEXT_FAST_RATES
+	/* B115k2 */ 4,
+#else
+	/* B1200  */ 384,
+#endif
+	/* B1800  */ 256,
+	/* B2400  */ 192,
+	/* B4800  */ 96,
+	/* B9600  */ 48,
+	/* B19200 */ 24,
+	/* B38400 */ 12,   /* The last of the standard rates.  */
+	/* B57600 */ 8,    /* ASYNC_SPD_HI                     */
+	/* B115K2 */ 4     /* ASYNC_SPD_VHI                    */
+};
+
+/* Number of detected ports.  */
+static int num_ioext = 0;
+
+/*
+ * Functions
+ *
+ * 'info' points to the first io-extender port.
+ *        There are (num_ioext * 2) entries in this array.
+ */
+static void ser_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct async_struct *info_table = dev_id;
+  u_char i;
+  IOEXT_struct *board = (IOEXT_struct *)info_table[0].board_base;
+
+  /* Check if any irq is pending for the current board.            */
+  /* Note: The IRQ_PEND-bit=0 if an irq is pending.                */
+
+#ifdef IRQ_DEBUG_2
+  printk("ioext: ser_interrupt()\n");
+#endif
+
+  if (!(board->CNTR & GVP_IRQ_PEND)) {
+#ifdef IRQ_DEBUG_2
+    printk("ser_interrupt() - no GVP_IRQ_PEND\n");
+#endif
+    return;
+  }
+
+  for (i = 0; i < 2; i++) {
+    u_char iir;
+    u_char lsr;
+    int ch;
+    struct async_struct *info = &info_table[i];
+    volatile struct uart_16c550 *uart = curruart(info);
+
+    iir = uart->IIR;
+
+#ifdef IRQ_DEBUG
+    printk("ttyS%d: IER=%02X, IIR=%02X, LSR=%02X, MSR=%02X\n", i, uart->IER, iir, uart->LSR, uart->MSR);
+#endif
+
+    while (!(iir & IRQ_PEND)) {
+      /* IRQ for this uart */
+#ifdef IRQ_DEBUG
+      printk("IRQ_PEND on ttyS%i...\n", i);
+#endif
+
+      switch (iir & (IRQ_ID1 | IRQ_ID2 | IRQ_ID3)) {
+        case IRQ_RLS: /* Receiver Line Status */
+#ifdef DEBUG_FLOW
+        printk("RLS irq on ttyS%i\n", i);
+#endif
+        case IRQ_CTI: /* Character Timeout */
+        case IRQ_RDA: /* Received Data Available */
+#ifdef IRQ_DEBUG
+        printk("irq (IIR=%02X) on ttyS%i\n", i, iir);
+#endif
+        /*
+         * Copy chars to the tty-queue ...
+         * Be careful that we aren't passing one of the
+         * Receiver Line Status interrupt-conditions without noticing.
+         */
+        {
+          int got = 0;
+
+          lsr = uart->LSR;
+#ifdef IRQ_DEBUG
+          printk("uart->LSR & DR = %02X\n", lsr & DR);
+#endif
+          while (lsr & DR) {
+            u_char err = 0;
+            ch = uart->RBR;
+#ifdef IRQ_DEBUG
+            printk("Read a char from the uart: %02X, lsr=%02X\n", ch, lsr);
+#endif
+            if (lsr & BI) {
+              err = TTY_BREAK;
+            }
+            else if (lsr & PE) {
+              err = TTY_PARITY;
+            }
+            else if (lsr & OE) {
+              err = TTY_OVERRUN;
+            }
+            else if (lsr & FE) {
+              err = TTY_FRAME;
+            }
+#ifdef DEBUG_IRQ
+            printk("rs_receive_char(ch=%02X, err=%02X)\n", ch, err);
+#endif
+            rs_receive_char(info, ch, err);
+            got++;
+            lsr = uart->LSR;
+          }
+#ifdef DEBUG_FLOW
+          printk("[%d<]", got);
+#endif
+        }
+        break;
+
+      case IRQ_THRE: /* Transmitter holding register empty */
+        {
+          int fifo_space = 16;
+          int sent = 0;
+
+#ifdef IRQ_DEBUG
+          printk("THRE-irq for ttyS%i\n", i);
+#endif
+
+          /* If the uart is ready to receive data and there are chars in */
+          /* the queue we transfer all we can to the uart's FIFO         */
+          if (info->xmit_cnt <= 0 || info->tty->stopped ||
+              info->tty->hw_stopped) {
+            /* Disable transmitter empty interrupt */
+            uart->IER &= ~(ETHREI);
+            /* Need to send a char to acknowledge the interrupt */
+            uart->THR = 0;
+#ifdef DEBUG_FLOW
+            if (info->tty->hw_stopped) {
+              printk("[-]");
+            }
+            if (info->tty->stopped) {
+              printk("[*]");
+            }
+#endif
+            break;
+          }
+
+          /* Handle software flow control */
+          if (info->x_char) {
+#ifdef DEBUG_FLOW
+#if 0
+            printk("Sending %02X to the uart.\n", info->x_char);
+#else
+            printk("[^%c]", info->x_char + '@');
+#endif
+#endif
+            uart->THR = info->x_char;
+            info->x_char = 0;
+            fifo_space--;
+            sent++;
+          }
+
+          /* Fill the fifo */
+          while (fifo_space > 0) {
+            fifo_space--;
+#ifdef IRQ_DEBUG
+            printk("Sending %02x to the uart.\n", info->xmit_buf[info->xmit_tail]);
+#endif
+            uart->THR = info->xmit_buf[info->xmit_tail++];
+            sent++;
+            info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+            if (--info->xmit_cnt == 0) {
+              break;
+            }
+          }
+#ifdef DEBUG_FLOW
+            printk("[>%d]", sent);
+#endif
+
+          if (info->xmit_cnt == 0) {
+#ifdef IRQ_DEBUG
+            printk("Sent last char - turning off THRE interrupts\n");
+#endif
+            /* Don't need THR interrupts any more */
+            uart->IER &= ~(ETHREI);
+          }
+
+          if (info->xmit_cnt < WAKEUP_CHARS) {
+            rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+          }
+        }
+        break;
+
+      case IRQ_MS: /* Must be modem status register interrupt? */
+        {
+          u_char msr = uart->MSR;
+#ifdef IRQ_DEBUG
+          printk("MS-irq for ttyS%i: %02x\n", i, msr);
+#endif
+
+          if (info->flags & ASYNC_INITIALIZED) {
+            if (msr & DCTS) {
+              rs_check_cts(info, (msr & CTS)); /* active high */
+#ifdef DEBUG_FLOW
+              printk("[%c-%d]", (msr & CTS) ? '^' : 'v', info->tty ? info->tty->hw_stopped : -1);
+#endif
+            }
+            if (msr & DDCD) {
+#ifdef DEBUG
+             printk("rs_dcd_changed(%d)\n", !(msr & DCD));
+#endif
+              rs_dcd_changed(info, !(msr & DCD)); /* active low */
+            }
+          }
+        }
+        break;
+
+      default:
+#ifdef IRQ_DEBUG
+          printk("Unexpected irq for ttyS%i\n", i);
+#endif
+        break;
+      } /* switch (iir) */
+      iir = uart->IIR;
+    } /* while IRQ_PEND */
+  } /* for i = 0, i < 2 */
+}
+
+
+static void ser_init(struct async_struct *info){
+
+#ifdef DEBUG
+  printk("ser_init\n");
+#endif
+
+  while ((curruart(info)->LSR) & DR){
+#if IOEXT_DEBUG
+    printk("Emptying uart\n");
+#endif
+    (void)curruart(info)->RBR;
+  }
+
+  /* Set DTR and RTS */
+  curruart(info)->MCR |= (DTR | RTS | OUT2);
+
+  /* Enable interrupts. IF_EXTER irq has already been enabled in ioext_init()*/
+  /* DON'T enable ETHREI here because there is nothing to send yet (murray) */
+  curruart(info)->IER |= (ERDAI | ELSI | EMSI);
+}
+
+
+static void ser_deinit(struct async_struct *info, int leave_dtr){
+
+#ifdef DEBUG
+  printk("ser_deinit\n");
+#endif
+
+  /* Wait for the uart to get empty */
+  while(!(curruart(info)->LSR & TEMT)) {
+#if IOEXT_DEBUG
+    printk("Waiting for the transmitter to finish\n");
+#endif
+  }
+
+  while(curruart(info)->LSR & DR) {
+#if IOEXT_DEBUG
+    printk("Uart not empty - flushing!\n");
+#endif
+    (void)curruart(info)->RBR;
+  }
+
+  /* No need to disable UART interrupts since this will already
+   * have been done via ser_enab_tx_int() and ser_stop_receive()
+   */
+
+  ser_RTSoff(info);
+  if (!leave_dtr)
+    ser_DTRoff(info);
+
+}
+
+/*
+** ser_enab_tx_int()
+** 
+** Enable or disable tx interrupts.
+** Note that contrary to popular belief, it is not necessary to
+** send a character to cause an interrupt to occur. Whenever the
+** THR is empty and THRE interrupts are enabled, an interrupt will occur.
+** (murray)
+*/
+static void ser_enab_tx_int(struct async_struct *info, int enab_flag){
+
+  if (enab_flag) {
+    curruart(info)->IER |= ETHREI;
+  }
+  else {
+    curruart(info)->IER &= ~(ETHREI);
+  }
+}
+
+static int  ser_check_custom_divisor(struct async_struct *info,
+				     int baud_base, int divisor)
+{
+  /* Always return 0 or else setserial spd_hi/spd_vhi doesn't work */
+  return 0;
+}
+
+static void ser_change_speed(struct async_struct *info){
+
+  u_int cflag, baud, chsize, stopb, parity, aflags;
+  u_int div = 0, ctrl = 0;
+
+#ifdef DEBUG
+  printk("ser_change_speed\n");
+#endif
+
+  if (!info->tty || !info->tty->termios) return;
+
+  cflag  = info->tty->termios->c_cflag;
+  baud   = cflag & CBAUD;
+  chsize = cflag & CSIZE;
+  stopb  = cflag & CSTOPB;
+  parity = cflag & (PARENB | PARODD);
+  aflags = info->flags & ASYNC_SPD_MASK;
+
+  if (cflag & CRTSCTS)
+      info->flags |= ASYNC_CTS_FLOW;
+  else
+      info->flags &= ~ASYNC_CTS_FLOW;
+  if (cflag & CLOCAL)
+      info->flags &= ~ASYNC_CHECK_CD;
+  else
+      info->flags |= ASYNC_CHECK_CD;
+
+#ifdef DEBUG
+  printk("Changing to baud-rate %i\n", baud);
+#endif
+
+  if (baud & CBAUDEX) {
+    baud &= ~CBAUDEX;
+    if (baud < 1 || baud > 2)
+      info->tty->termios->c_cflag &= ~CBAUDEX;
+    else
+      baud += 15;
+  }
+  if (baud == 15){
+    if (aflags == ASYNC_SPD_HI)   /*  57k6 */
+      baud += 1;
+    if (aflags == ASYNC_SPD_VHI)  /* 115k2 */
+      baud += 2;
+    if (aflags == ASYNC_SPD_CUST)
+      div = info->custom_divisor;
+  }
+  if (!div){
+      /* Maximum speed is 115200 */
+      if (baud > 17) baud = 17;
+      div = ioext_baud_table[baud];
+  }
+
+  if (!div) {
+    /* speed == 0 -> drop DTR */
+#ifdef DEBUG
+    printk("Dropping DTR\n");
+#endif
+    ser_DTRoff(info);
+    return;
+  }
+
+  /*
+   * We have to set DTR when a valid rate is chosen, otherwise DTR
+   * might get lost when programs use this sequence to clear the line:
+   *
+   * change_speed(baud = B0);
+   * sleep(1);
+   * change_speed(baud = Bx); x != 0
+   *
+   * The pc-guys do this aswell.
+   */
+  ser_DTRon(info);
+
+  if (chsize == CS8){
+#ifdef DEBUG
+    printk("Setting serial word-length to 8-bits\n");
+#endif
+    ctrl |= data_8bit;
+  } else if (chsize == CS7){
+#ifdef DEBUG
+    printk("Setting serial word-length to 7-bits\n");
+#endif
+    ctrl |= data_7bit;
+  } else if (chsize == CS6){
+#ifdef DEBUG
+    printk("Setting serial word-length to 6-bits\n");
+#endif
+    ctrl |= data_6bit;
+  } else if (chsize == CS5){
+#ifdef DEBUG
+    printk("Setting serial word-length to 5-bits\n");
+#endif
+    ctrl |= data_5bit;
+   };
+
+
+  /* If stopb is true we set STB which means 2 stop-bits - */
+  /* otherwise we  only get 1 stop-bit.                    */
+  ctrl |= (stopb ? STB : 0);
+     
+  ctrl |= ((parity & PARENB) ? ((parity & PARODD) ? (PEN) : (PEN |
+							     EPS)) :
+	   0x00 ); 
+
+#ifdef DEBUG
+  printk ("Storing serial-divisor %i\n", div);
+#endif
+
+  curruart(info)->LCR = (ctrl | DLAB);
+
+  /* Store high byte of divisor */
+  curruart(info)->DLM = ((div >> 8) & 0xff);
+  
+  /* Store low byte of divisor */
+
+  curruart(info)->DLL = (div & 0xff);
+
+  curruart(info)->LCR = ctrl;
+}
+
+
+static void ser_throttle(struct async_struct *info, int status){
+
+#ifdef DEBUG
+  printk("ser_throttle\n");
+#endif
+  if (status){
+    ser_RTSoff(info);
+  }
+  else{
+    ser_RTSon(info);
+  }
+}
+
+
+static void ser_set_break(struct async_struct *info, int break_flag){
+
+#if IOEXT_DEBUG
+  printk("ser_set_break\n");
+#endif
+    if (break_flag)
+	curruart(info)->LCR |= SET_BREAK;
+    else
+	curruart(info)->LCR &= ~SET_BREAK;
+}
+
+
+static void ser_get_serial_info(struct async_struct *info,
+				struct serial_struct *retinfo){
+#ifdef DEBUG
+  printk("ser_get_serial_info\n");
+#endif
+
+
+  retinfo->baud_base = IOEXT_BAUD_BASE;
+  retinfo->xmit_fifo_size = FIFO_SIZE; /* This field is currently ignored, */
+				/* by the upper layers of the       */
+				/* serial-driver.                   */
+  retinfo->custom_divisor = info->custom_divisor;
+
+}
+
+static unsigned int ser_get_modem_info(struct async_struct *info){
+
+  u_char msr, mcr;
+
+#ifdef DEBUG
+  printk("ser_get_modem_info\n");
+#endif
+
+  msr = curruart(info)->MSR;
+  mcr = curruart(info)->MCR; /* The DTR and RTS are located in the */
+			     /* ModemControlRegister ...           */
+
+ return(
+	 ((mcr & DTR) ? TIOCM_DTR : 0) |
+	 ((mcr & RTS) ? TIOCM_RTS : 0) |
+
+	 ((msr & DCD) ? 0 : TIOCM_CAR) | /* DCD is active low */
+	 ((msr & CTS) ? TIOCM_CTS : 0) |
+	 ((msr & DSR) ? TIOCM_DSR : 0) |
+	 ((msr & RING_I) ? TIOCM_RNG : 0)
+	 );
+}
+
+static int ser_set_modem_info(struct async_struct *info, int new_dtr,
+			      int new_rts){
+#ifdef DEBUG
+  printk("ser_set_modem_info new_dtr=%i new_rts=%i\n", new_dtr, new_rts);
+#endif
+  if (new_dtr == 0)
+    ser_DTRoff(info);
+  else if (new_dtr == 1)
+    ser_DTRon(info);
+
+  if (new_rts == 0){
+    ser_RTSoff(info);
+  }else{
+    if (new_rts == 1){
+      ser_RTSon(info);
+    }
+  }
+
+  return 0;
+};
+
+static void ser_stop_receive (struct async_struct *info)
+{
+  /* Disable uart receive and status interrupts */
+  curruart(info)->IER &= ~(ERDAI | ELSI | EMSI);
+}
+
+static int ser_trans_empty (struct async_struct *info)
+{
+  return (curruart(info)->LSR & THRE);
+}
+
+/*
+ * Detect and initialize all IO-Extenders in this system.
+ */
+int ioext_init(void)
+{
+  int key = 0;
+  struct ConfigDev *cd;
+  caddr_t address;
+  enum GVP_ident epc;
+  short i;
+  int line1, line2;
+  struct serial_struct req;
+  u_char isr_installed = 0;
+  IOEXT_struct *board;                  /* Used as a pointer to the    */
+				        /* board-address               */
+  IOEXT_struct *ioext_bases[MAX_IOEXT]; /* Just used to inform the     */
+					/* user of the location of the */
+					/* boards found during         */
+					/* configuration.              */     
+
+  if (!MACH_IS_AMIGA)
+      return -ENODEV;
+
+  for (i=0 ; i < MAX_IOEXT; i++)
+    ioext_bases[i] = NULL;
+
+  /*
+   * key++ is not needed anymore when a board is found, as zorro_find
+   * returns board_nr + 1 and it adds 1 to the index before it starts
+   * searching.
+   */
+  while ((key = zorro_find(MANUF_GVP, PROD_GVP, 0, key))) {
+
+    cd = zorro_get_board(key);
+
+    /* As GVP uses the same product code for several kind of boards, */
+    /* we have to check the extended product code, to see if this */
+    /* really is an io-extender. */
+    /* check extended product code */
+    address = cd->cd_BoardAddr;
+    epc = *(enum GVP_ident *)ZTWO_VADDR(address + 0x8000) & GVP_PRODMASK;
+
+    if (epc != GVP_IOEXT){
+      continue;
+    }
+
+    /* Wait for the board to be ready and disable everything */
+    board = (IOEXT_struct *) ZTWO_VADDR(address);
+
+    ioext_bases[num_ioext] = board;
+
+    while(!board->CNTR & GVP_IRQ_PEND) {
+	printk("GVP-irq pending \n");
+    }
+      
+    /*
+     * Disable board-interrupts for the moment.
+     */
+    board->CNTR = 0;
+
+    /*
+     * Setting the necessary tty-stuff.
+     */
+    req.line              = -1; /* first free ttyS? device */
+    req.type              = SER_IOEXT;
+    req.port              = (int) &board->uart0;
+    if ((line1 = register_serial( &req )) < 0) {
+	printk( "Cannot register IO-Extender serial port: no free device\n" );
+	return -EBUSY;
+    }
+    rs_table[line1].nr_uarts = 0;
+    rs_table[line1].board_base = board;
+    rs_table[line1].sw = &ioext_ser_switch;
+
+    req.line              = -1; /* first free ttyS? device */
+    req.type              = SER_IOEXT;
+    req.port              = (int) &board->uart1;
+    if ((line2 = register_serial( &req )) < 0) {
+	printk( "Cannot register IO-Extender serial port: no free device\n" );
+	unregister_serial( line1 );
+	return -EBUSY;
+    }
+    rs_table[line2].nr_uarts = 1;
+    rs_table[line2].board_base = board;
+    rs_table[line2].sw = &ioext_ser_switch;
+
+    /* Install ISR if it hasn't been installed already */
+    if (!isr_installed) {
+	request_irq(IRQ_AMIGA_EXTER, ser_interrupt, 0,
+	            "io-extender serial", rs_table + line1);
+	isr_installed++;
+    }
+
+    /*
+     * Set the board to INT6 and RTSx port-control.
+     * (GVP uses this setup).
+     */
+    board->CTRL = (IRQ_SEL|PORT0_CTRL|PORT1_CTRL);
+
+    /* Add {} in here so that without debugging we still get the
+     * desired effect (murray)
+     */
+
+    /* Wait for the uarts to get empty */
+    while(!(board->uart0.LSR & TEMT)) {
+#if IOEXT_DEBUG
+	printk("Waiting for transmitter 0 to finish\n");
+#endif
+    }
+
+    /* Wait for the uart to get empty */
+    while(!(board->uart1.LSR & TEMT)) {
+#if IOEXT_DEBUG
+	printk("Waiting for transmitter 1 to finish\n");
+#endif
+    }
+
+    /*
+     * Set the uarts to a default setting og 8N1 - 9600
+     */
+
+    board->uart0.LCR = (data_8bit | DLAB);
+    board->uart0.DLM = 0;
+    board->uart0.DLL = 48;
+    board->uart0.LCR = (data_8bit);
+
+    board->uart1.LCR = (data_8bit | DLAB);
+    board->uart1.DLM = 0;
+    board->uart1.DLL = 48;
+    board->uart1.LCR = (data_8bit);
+
+
+    /*
+     * Enable + reset both the tx and rx FIFO's.
+     * Sett the rx FIFO-trigger count.
+     */
+
+    board->uart0.FCR =  ( FIFO_ENA      | RCVR_FIFO_RES |
+		          XMIT_FIFO_RES | FIFO_TRIGGER_LEVEL );
+
+    board->uart1.FCR =  ( FIFO_ENA      | RCVR_FIFO_RES |
+			  XMIT_FIFO_RES | FIFO_TRIGGER_LEVEL );
+
+    /* 
+     * Initialise parallel port.
+     */
+    board->par.CTRL = (INT2EN | SLIN | INIT);
+
+    /*
+     * Master interrupt enable - National semconductors doesn't like
+     * to follow standards, so their version of the chip is
+     * different and I only had a copy of their data-sheets :-(
+     */
+    board->uart0.MCR = OUT2;
+    board->uart1.MCR = OUT2;
+
+    /*
+     * Disable all uart interrups (they will be re-enabled in
+     * ser_init when they are needed).
+     */
+    board->uart0.IER = 0;
+    board->uart1.IER = 0;
+
+
+    num_ioext++;
+    zorro_config_board(key, 0);
+
+    /*
+     * Re-enable all IRQ's for the current board (board interrupt).
+     */
+    board->CNTR |= GVP_IRQ_ENA;
+  }
+
+  /* Print number of IO-Extenders found and their base-addresses.*/
+  if (num_ioext > 0){
+    printk ("Detected %i GVP IO-Extender(s) at: ", num_ioext);
+    for (i = 0 ; i < num_ioext ; i++){
+      printk ("0x%08x", (unsigned int) ioext_bases[i]);
+      if (i+1 < num_ioext)
+	printk(", ");
+    }
+    printk("\n");
+  }
+
+  return(0);
+}
diff -urN pc-2.0.21/drivers/char/ser_ioext.h linux-2.0.21/drivers/char/ser_ioext.h
--- pc-2.0.21/drivers/char/ser_ioext.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/ser_ioext.h	Sat Aug 24 14:35:04 1996
@@ -0,0 +1,68 @@
+/*
+ * Defines for the GVP IO-Extender (duart, midi, parallel board), for
+ * the Amiga range of computers.
+ *
+ * This code is (C) 1995 Jes Sorensen (jds@kom.auc.dk).
+ */
+
+#ifndef _IO_EXTENDER_H_
+#define _IO_EXTENDER_H_
+
+#include "16c552.h"
+
+#define MAX_IOEXT 5 /*
+		     * The maximum number of io-extenders is 5, as you
+		     * can't have more than 5 ZII boards in any Amiga.
+		     */
+
+#define UART_CLK 7372800
+
+#define IOEXT_BAUD_BASE (UART_CLK / 16)
+
+#define curruart(info) ((struct uart_16c550 *)(info->port))
+
+#define ser_DTRon(info)  curruart(info)->MCR |=  DTR
+#define ser_RTSon(info)  curruart(info)->MCR |=  RTS
+#define ser_DTRoff(info) curruart(info)->MCR &= ~DTR
+#define ser_RTSoff(info) curruart(info)->MCR &= ~RTS
+
+
+/*
+ * This is the struct describing the registers on the IO-Extender.
+ * NOTE: The board uses a dual uart (16c552), which should be equal to
+ * two 16c550 uarts.
+ */
+typedef struct {
+  u_char gap0[0x41];
+  volatile u_char CNTR;        /* GVP DMAC CNTR (status register)     */
+  u_char gap1[0x11e];
+  struct uart_16c550 uart0;    /* The first uart                      */
+  u_char gap2[0xf0];
+  struct uart_16c550 uart1;    /* The second uart                     */
+  u_char gap3[0xf0];
+  struct IOEXT_par par;        /* The parallel port                   */
+  u_char gap4[0xfb];
+  volatile u_char CTRL;       /* The control-register on the board   */
+} IOEXT_struct;
+
+/*
+ * CNTR defines (copied from the GVP SCSI-driver file gvp11.h
+ */
+#define GVP_BUSY	(1<<0)
+#define GVP_IRQ_PEND	(1<<1)
+#define GVP_IRQ_ENA 	(1<<3)
+#define GVP_DIR_WRITE   (1<<4)
+
+/*
+ * CTRL defines 
+ */
+#define PORT0_MIDI   (1<<0)  /* CLR = DRIVERS         SET = MIDI      */
+#define PORT1_MIDI   (1<<1)  /* CLR = DRIVERS         SET = MIDI      */
+#define PORT0_DRIVER (1<<2)  /* CLR = RS232,          SET = MIDI      */
+#define PORT1_DRIVER (1<<3)  /* CLR = RS232,          SET = MIDI      */
+#define IRQ_SEL      (1<<4)  /* CLR = INT2,           SET = INT6      */
+#define ROM_BANK_SEL (1<<5)  /* CLR = LOW 32K,        SET = HIGH 32K  */
+#define PORT0_CTRL   (1<<6)  /* CLR = RTSx or RXRDYx, SET = RTSx ONLY */
+#define PORT1_CTRL   (1<<7)  /* CLR = RTSx or RXRDYx, SET = RTSx ONLY */
+
+#endif
diff -urN pc-2.0.21/drivers/char/ser_mfc.c linux-2.0.21/drivers/char/ser_mfc.c
--- pc-2.0.21/drivers/char/ser_mfc.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/ser_mfc.c	Sat Aug 24 14:35:04 1996
@@ -0,0 +1,715 @@
+
+/* serial driver for the two serial ports of a Multiface Card III
+ *
+ * Might also work with a SerialMaster or Multiface II, as far as I know
+ * the only difference is the frequency of the used quartz.
+ * Test it, and let me know if it works. Changing the baud table isn't worth
+ * whole new driver.
+ * (To hardware hackers: It should be possible to get 115200 Baud with the older 
+ * card by replacing the 3.6864 MHz quartz with a 7.3728 MHz one. But beware!
+ * The only one who knows that now effectivly all baudrates are doubled are you
+ * and not any application. Besides, you should know what you are doing and you do it
+ * at your own risk.)
+ *
+ * Due to hardware constraints I decided to make the two ports of the card different.
+ * Port0 (the 9-pin-one) is only able to handle the following baud rates:
+ * 150, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400 
+ * (MFC II and Serial Master can do all standard rates.)
+ * Port1 (the 25-pin-one) can handle any approbriate custom baud rate.
+ * Tell me if you would prefer it the other way and why. Perhaps I'll make it
+ * configurable.
+ * 
+ * Another solution would be to handle both ports the same way.
+ * But that would mean to support only standart baud rates or to get into
+ * big difficulties. On the other hand, you had more standard rates available.
+ *
+ * MIDI is missed with 5%. Can anyone tell me how to figure out which quartz
+ * is attached by software? I think the duart.device simply assumes 4.000MHz
+ * when you select MIDI baudrate.
+ *
+ * Another Hardware lack: If you select 5 bit character size a halve stop bit 
+ * is sent too much.
+ *
+ * Hint to BSC: If they had used the IP2/3 pins for DCD and IP4/5 for DSR,
+ * I could use the change state interrupt feature of the duart instead of polling
+ * the DCD every vblank-interrupt. (Or does someone want to be interrupted when
+ * DSR changes state (i.e. modem switched on/off) instead of knowing when a 
+ * connection is established/lost?)
+ *
+ * Note: The duart is able to handle the RTS/CTS-protocol in hardware.
+ * I use this feature to prevent receiver overruns by letting the DUART drop RTS
+ * when the FIFO is full
+ *
+ * created 15.11.95 Joerg Dorchain (dorchain@mpi-sb.mpg.de)
+ *
+ * adapted 11.12.95 for 1.2.13 Joerg Dorchain
+ *
+ * improved 6. 2.96 Joerg Dorchain
+ *  -  fixed restart bug
+ *
+ */
+
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/serial.h>
+
+#include <asm/setup.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/amigatypes.h>
+#include <asm/zorro.h>
+#include <asm/irq.h>
+
+#include "multiface.h"
+#include "mc68681.h"
+
+/* how many cards are to be supported */
+#define MAX_CARD 5
+
+/***************************** Prototypes *****************************/
+
+static void mfc_interrupt(int irq, void *data, struct pt_regs *fp);
+static void mfc_vbl_inter(int irq, void *data, struct pt_regs *fp);
+static void mfc_init( struct async_struct *info );
+static void mfc_deinit( struct async_struct *info, int leave_dtr );
+static void mfc_enab_tx_int( struct async_struct *info, int enab_flag );
+static int  mfc_check_custom_divisor( struct async_struct *info, int
+				     baud_base, int divisor );
+static void mfc_change_speed( struct async_struct *info );
+static void mfc_throttle( struct async_struct *info, int status );
+static void mfc_set_break( struct async_struct *info, int break_flag );
+static void mfc_get_serial_info( struct async_struct *info,
+				struct serial_struct *retinfo );
+static unsigned int mfc_get_modem_info( struct async_struct *info );
+static int mfc_set_modem_info( struct async_struct *info, int new_dtr,
+			      int new_rts );
+static void mfc_stop_receive(struct async_struct *info);
+static int mfc_trans_empty(struct async_struct *info);
+
+/************************* End of Prototypes **************************/
+
+
+
+/* SERIALSWITCH structure for the Multiface serial ports */
+
+static SERIALSWITCH mfc_ser_switch = {
+    mfc_init, mfc_deinit, mfc_enab_tx_int,
+    mfc_check_custom_divisor, mfc_change_speed,
+    mfc_throttle, mfc_set_break,
+    mfc_get_serial_info, mfc_get_modem_info,
+    mfc_set_modem_info, NULL,
+    mfc_stop_receive, mfc_trans_empty, NULL
+};
+
+
+
+/* These value go into the csr (Clock Select Registers) */
+/* 0xff means not available, B0 is handle special anyway */
+ static u_char fixed_baud_III[16] = {
+ 0, /* B0 */
+ 0xff, /* B50 */
+ 0xff, /* B75 */
+ 0xff, /* B110 */
+ 0xff, /* B134 */
+ 0xff, /* B150 */
+ 0xff, /* B200 */
+ 0x33, /* B300 */
+ 0x44, /* B600 */
+ 0x55, /* B1200 */
+ 0xff, /* B1800 */
+ 0x66, /* B2400 */
+ 0x88, /* B4800 */
+ 0x99, /* B9600 */
+ 0xbb, /* B19200 */
+ 0xcc  /* B38400 */
+ };
+/* These are precalculated timer values for standart rates */
+ static u_short custom_baud_III[18] = {
+ 0, /* B0 */
+ 4608, /* B50 */
+ 3072, /* B75 */
+ 2095, /* B110 - a little bit off (0.02%) */
+ 1713, /* B134 - off by 0.0006% */
+ 1536, /* B150 */
+ 1152, /* B200 */
+ 768, /* B300 */
+ 384, /* B600 */
+ 192, /* B1200 */
+ 128, /* B1800 */
+ 96, /* B2400 */
+ 48, /* B4800 */
+ 24, /* B9600 */
+ 12, /* B19200 */
+ 6, /* B38400 */
+ 4, /* B57600 */
+ 2 /* B115200 */
+ };
+
+static int nr_mfc; /* nr of ports configured */
+static int lines[MAX_CARD * 2]; /* accociated tty lines (index in rs_table) */
+static int board_index[MAX_CARD]; /* nr. of zorro slot */
+static u_char imask[MAX_CARD];
+static u_char acmask[MAX_CARD];
+
+static void mfc_fill_tx(struct async_struct *info)
+{
+struct duart *dp;
+struct duarthalf *d;
+int sta,i,mask,ch;
+
+dp=info->board_base;
+d=(struct duarthalf *)info->port;
+i=(info->nr_uarts-1)/2;
+mask=(((info->nr_uarts-1)%2)==0)?1:16;
+sta=d->sr_csr.sr;
+while ((sta & 4) != 0) {   /* fill the buffer */
+  if ((ch = rs_get_tx_char( info )) >= 0)
+    d->hr.thr = ch;
+  else
+    break;
+  sta=d->sr_csr.sr;
+}
+if (rs_no_more_tx( info ))
+  imask[i] &= ~mask;
+else
+  imask[i] |= mask;
+dp->ir.imr = imask[i];
+}
+
+static u_char isr_entered = 0;
+
+static void mfc_interrupt(int irq, void *data, struct pt_regs *fp)
+{
+int i;
+int ireq;
+struct duart *dp;
+int ch;
+int status;
+int err;
+int ipch;
+u_short intenar;
+
+
+intenar = (custom.intenar & IF_EXTER);
+custom.intena = IF_EXTER;
+if (isr_entered) {
+  printk("Reentering MFC isr.\n");
+  custom.intena = (intenar | IF_SETCLR);
+  return;
+}
+isr_entered = 1;
+
+custom.intena = (intenar | IF_SETCLR);
+
+for (i = 0; i < nr_mfc; i += 2) {
+  dp = rs_table[lines[i]].board_base;
+  ireq = dp->ir.isr;
+  if ((ireq & imask[i/2]) !=0) { /* if it is "our" interrupt */
+    /* first the transmitter */
+    mfc_fill_tx(rs_table + lines[i]);      /* channel A */
+    mfc_fill_tx(rs_table + lines[i + 1]);  /* channel B */
+    { /* test for receiver ready */
+      {
+	status = dp->pa.sr_csr.sr; /* empty the FIFO */
+	while ((status & 1) != 0) {
+	  ch = dp->pa.hr.rhr;
+	  err = 0;
+	  if ((status & SR_BREAK) != 0)
+	    err |= TTY_BREAK;
+	  if ((status & SR_FRAMING) != 0)
+	    err |= TTY_FRAME;
+	  if ((status & SR_PARITY) != 0)
+	    err |= TTY_PARITY;
+	  if ((status & SR_OVERRUN) != 0)
+	    err |= TTY_OVERRUN;
+	  rs_receive_char(rs_table + lines[i], ch, err);
+	  status = dp->pa.sr_csr.sr;
+	} 
+      }
+      {
+	status = dp->pb.sr_csr.sr;
+	while (( status & 1) != 0) {
+	  ch = dp->pb.hr.rhr;
+	  err = 0;
+	  if ((status & SR_BREAK) != 0)
+	    err |= TTY_BREAK;
+	  if ((status & SR_FRAMING) != 0)
+	    err |= TTY_FRAME;
+	  if ((status & SR_PARITY) != 0)
+	    err |= TTY_PARITY;
+	  if ((status & SR_OVERRUN) != 0)
+	    err |= TTY_OVERRUN;
+	  rs_receive_char(rs_table + lines[i + 1], ch, err);
+	  status = dp->pb.sr_csr.sr;
+	} 
+      }
+    }
+    { /* CTS changed */
+     ipch = dp->ipcr_acr.ipcr;
+     if ((ipch & 16) !=0)  /* port a */
+       rs_check_cts(rs_table + lines[i], !(ipch & 1)); 
+     if ((ipch & 32) !=0)
+       rs_check_cts(rs_table + lines[i + 1], !(ipch & 2));
+    }
+  }
+}
+custom.intreq = IF_EXTER;
+isr_entered = 0;
+}
+
+static u_char curr_dcd[MAX_CARD];
+
+static void mfc_vbl_inter(int irq, void *data, struct pt_regs *fp)
+{
+int i;
+struct duart *dp;
+u_char bits;
+
+for (i = 0; i < nr_mfc; i += 2) {
+  if (rs_table[lines[i]].flags & ASYNC_INITIALIZED) {
+    dp = rs_table[lines[i]].board_base;
+    bits = dp->ipr_opcr.ipr & 48;
+    if (bits ^ curr_dcd[i/2]) {
+      if (((bits ^ curr_dcd[i/2]) & 16) != 0)
+	rs_dcd_changed(rs_table + lines[i], !(bits & 16));
+      if (((bits ^ curr_dcd[i/2]) & 32) != 0)
+	rs_dcd_changed(rs_table + lines[i + 1], !(bits & 32));
+    }
+    curr_dcd[i/2] = bits;
+  }
+}
+}
+
+static void mfc_init( struct async_struct *info)
+{
+struct duart *dp;
+struct duarthalf *d;
+
+dp = info->board_base;
+d = (struct duarthalf *)info->port;
+if (((info->nr_uarts-1)%2) == 0) {  /* port A */
+  dp->start_sopc.sopc = 5;		/* DTR & RTS on */
+  imask[(info->nr_uarts-1)/2] |= 3|128; /* interrupts on */
+  acmask[(info->nr_uarts-1)/2] |= 1;
+}
+else {
+  dp->start_sopc.sopc = 10;
+  imask[(info->nr_uarts-1)/2] |= 48|128;
+  acmask[(info->nr_uarts-1)/2] |= 2;
+}
+d->cr = CR_RESET_RX;
+d->cr = CR_RESET_TX;
+d->cr = CR_RESET_ERR;
+d->cr = CR_RESET_BREAK;
+d->cr = CR_STOP_BREAK;
+d->cr = CR_RX_ON;
+d->cr = CR_TX_ON;
+curr_dcd[(info->nr_uarts-1)/2] = dp->ipr_opcr.ipr & 48;
+custom.intena = IF_EXTER;
+/* enable interrupts */
+dp->ir.imr = imask[(info->nr_uarts-1)/2];
+dp->ipcr_acr.acr = acmask[(info->nr_uarts-1)/2];
+custom.intena = (IF_EXTER | IF_SETCLR);
+MOD_INC_USE_COUNT;
+}
+
+static void mfc_deinit( struct async_struct *info, int leave_dtr)
+{
+struct duart *dp;
+struct duarthalf *d;
+u_short intenar;
+
+dp = info->board_base;
+d = (struct duarthalf *)info->port;
+/* turn off interrupts CTS and DTR if required */
+if (((info->nr_uarts-1)%2) == 0) {
+  dp->stop_ropc.ropc = 1;
+  if (!leave_dtr)
+    dp->stop_ropc.ropc = 4;
+  imask[(info->nr_uarts-1)/2] &= ~3;
+  acmask[(info->nr_uarts-1)/2] &= ~1;
+}
+else {
+  dp->stop_ropc.ropc = 2;
+  if (!leave_dtr)
+    dp->stop_ropc.ropc = 8;
+  imask[(info->nr_uarts-1)/2] &= ~48;
+  acmask[(info->nr_uarts-1)/3] &= ~2;
+}
+intenar = (custom.intenar & IF_EXTER);
+custom.intena = IF_EXTER;
+dp->ir.imr = imask[(info->nr_uarts-1)/2];
+dp->ipcr_acr.acr = acmask[(info->nr_uarts-1)/2];
+custom.intena = (intenar | IF_SETCLR);
+/* disable transmitter and receiver after current character */
+d->cr = CR_RX_OFF;
+d->cr = CR_TX_OFF;
+MOD_DEC_USE_COUNT;
+}
+
+static void mfc_enab_tx_int(struct async_struct *info, int enab_flag)
+{
+struct duart *dp;
+
+if (enab_flag)
+  mfc_fill_tx(info); /* fills the tx buf and enables interrupts */
+else { /* disable interrupts */
+  /* we could also disable the transmitter at all */
+  dp = info->board_base;
+  if (((info->nr_uarts-1)%2) == 0) 
+    imask[(info->nr_uarts-1)/2] &= ~1;
+  else
+    imask[(info->nr_uarts-1)/2] &= ~16;
+  dp->ir.imr=imask[(info->nr_uarts-1)/2];
+}
+}
+
+static int mfc_check_custom_divisor(struct async_struct *info,
+      int baud_base, int divisor)
+{
+if (((info->nr_uarts-1)%2) == 0) 
+  return 1;
+if (baud_base != 230400) /* change for MFC II */
+  return 1;
+if (divisor < 2)
+  return 1;
+return 0;
+}
+
+static void mfc_change_speed(struct async_struct *info)
+{
+u_int cflag, baud, chsize, stopb, parity, aflags;
+u_int ctrl = 0;
+u_short div = 0;
+struct duart *dp;
+struct duarthalf *d;
+int dummy;
+
+if (!info->tty || ! info->tty->termios)
+  return;
+dp = info->board_base;
+d = (struct duarthalf *)info->port;
+cflag = info->tty->termios->c_cflag;
+baud = cflag & CBAUD;
+chsize = cflag & CSIZE;
+stopb = cflag & CSTOPB;
+parity = cflag & (PARENB | PARODD);
+aflags = info->flags & ASYNC_SPD_MASK;
+
+if (cflag & CRTSCTS)
+  info->flags |= ASYNC_CTS_FLOW;
+else
+  info->flags &= ~ASYNC_CTS_FLOW;
+if (cflag & CLOCAL)
+  info->flags &= ~ASYNC_CHECK_CD;
+else
+  info->flags |= ASYNC_CHECK_CD;
+
+if (baud & CBAUDEX) {
+  baud &= ~CBAUDEX;
+  if (baud < 1 || baud >2)
+    info->tty->termios->c_cflag &= ~CBAUDEX;
+  else
+    baud += 15;
+}
+
+if (baud == 15) {
+  if (aflags == ASYNC_SPD_HI)
+    baud += 1;
+  if (aflags == ASYNC_SPD_VHI)
+    baud += 2;
+  if (aflags == ASYNC_SPD_CUST)
+    div = info->custom_divisor;
+}
+if (!div) {
+  if (baud > 17)
+    baud = 17;
+  div = custom_baud_III[baud];
+}
+
+if (!div) { /* drop DTR */
+  if (((info->nr_uarts-1)%2) == 0)
+    dp->stop_ropc.ropc = 4;
+  else
+    dp->stop_ropc.ropc = 8;
+  return;
+}
+
+if (((info->nr_uarts-1)%2) == 0) {
+  dp->start_sopc.sopc = 4; /* set DTR */
+  if (baud > 15)
+    baud =15;
+  if (fixed_baud_III[baud] != 0xff)
+    d->sr_csr.csr = fixed_baud_III[baud];
+  else
+    printk("ttyS%d: ignored illegal baudrate %d\n", info->line,baud);
+}
+else {
+  dp->start_sopc.sopc = 8;
+  dp->ctu = div / 256;
+  dp->ctl = div % 256;
+  dummy = dp->start_sopc.start;
+}
+d->mr.mr2 = (stopb) ? MR2_2STOP: MR2_1STOP;
+
+if (chsize == CS8)
+  ctrl |= MR1_8BITS;
+else if (chsize == CS7)
+  ctrl |= MR1_7BITS;
+else if (chsize == CS6)
+  ctrl |= MR1_6BITS;
+else if (chsize == CS5)
+  ctrl |= MR1_5BITS;
+
+if (parity & PARENB)
+  ctrl |= MR1_PARITY_WITH;
+else
+  ctrl |= MR1_PARITY_NO;
+if (parity & PARODD)
+  ctrl |= MR1_PARITY_ODD;
+
+d->cr = CR_RESET_MR;
+d->mr.mr1 = ctrl|MR1_RxRTS_ON;
+}
+
+static void mfc_throttle (struct async_struct *info, int status)
+{
+struct duart *dp;
+
+dp = info->board_base;
+if (((info->nr_uarts-1)%2) == 0) {
+  if (status)
+    dp->stop_ropc.ropc = 1;
+  else 
+    dp->start_sopc.sopc = 1;
+}
+else {
+  if (status)
+    dp->stop_ropc.ropc = 2;
+  else 
+    dp->start_sopc.sopc = 2;
+}
+}
+
+static void mfc_set_break (struct async_struct *info, int break_flag)
+{
+struct duarthalf *d;
+
+d = (struct duarthalf *)info->port;
+if (break_flag)
+  d->cr = CR_START_BREAK;
+else
+  d->cr = CR_STOP_BREAK;
+}
+
+static void mfc_get_serial_info( struct async_struct *info, 
+      struct serial_struct *retinfo)
+{
+if (((info->nr_uarts-1)%2) == 0) {
+  retinfo->baud_base = 0;
+  retinfo->custom_divisor = 0;
+}
+else {
+  retinfo->baud_base = 230400;
+  retinfo->custom_divisor = info->custom_divisor;
+}
+}
+
+static unsigned int mfc_get_modem_info(struct async_struct *info)
+{
+struct duart *dp;
+u_char inf;
+
+dp = info->board_base;
+inf = dp->ipr_opcr.ipr;
+if (((info->nr_uarts-1)%2) == 0) 
+  return ((inf & 1) ? 0 : TIOCM_CTS) |
+         ((inf & 4) ? 0 : TIOCM_DSR) |
+         ((inf & 16) ? 0 : TIOCM_CAR) |
+         ((dp->pa.ri & 1) ? 0 : TIOCM_RNG);
+else
+  return ((inf & 2) ? 0 : TIOCM_CTS) |
+         ((inf & 8) ? 0 : TIOCM_DSR) |
+         ((inf & 32) ? 0 : TIOCM_CAR) |
+         ((dp->pb.ri & 2) ? 0 : TIOCM_RNG);
+/* No chance to check DTR & RTS easily */
+}
+
+static int mfc_set_modem_info(struct async_struct *info, int new_dtr, int new_rts)
+{
+struct duart *dp;
+
+dp = info->board_base;
+if (((info->nr_uarts-1)%2) == 0) {
+  if (new_dtr == 0)
+    dp->stop_ropc.ropc = 4;
+  else if (new_dtr == 1)
+    dp->start_sopc.sopc = 4;
+  if (new_rts == 0)
+    dp->stop_ropc.ropc = 1;
+  else if (new_rts == 1)
+    dp->start_sopc.sopc = 1;
+}
+else {
+  if (new_dtr == 0)
+    dp->stop_ropc.ropc = 8;
+  else if (new_dtr == 1)
+    dp->start_sopc.sopc = 8;
+  if (new_rts == 0)
+    dp->stop_ropc.ropc = 2;
+  else if (new_rts == 1)
+    dp->start_sopc.sopc = 2;
+}
+return 0;
+}
+
+static void mfc_stop_receive(struct async_struct *info)
+{
+struct duart *dp;
+
+dp = info->board_base;
+if (((info->nr_uarts-1)%2) == 0)
+  imask[(info->nr_uarts-1)/2] &= ~2;
+else
+  imask[(info->nr_uarts-1)/2] &= ~32;
+dp-> ir.imr = imask[(info->nr_uarts-1)/2];
+}
+
+static int mfc_trans_empty(struct async_struct *info)
+{
+struct duarthalf *dh;
+
+dh = (struct duarthalf *)info->port;
+return (dh->sr_csr.sr & 8);
+}
+
+int multiface_init(void)
+{
+int i, line1, line2;
+struct duart *dp;
+int dummy;
+struct ConfigDev *cd;
+struct serial_struct req;
+struct async_struct *info;
+
+if (!MACH_IS_AMIGA)
+  return -ENODEV;
+  
+nr_mfc = 0;
+/* add MFC_II and serial master for test purposes */
+while((i=zorro_find(MANUF_BSC2, PROD_MULTIFACE_III,1, 0))) {
+  board_index[nr_mfc/2] = i;
+  cd = zorro_get_board(i);
+  dp = (struct duart *)ZTWO_VADDR((((u_char *)cd->cd_BoardAddr)+DUARTBASE));
+
+  req.line = -1; /* first free ttyS? device */
+  req.type = SER_MFC_III;
+  req.port = (int)&dp->pa;
+  if ((line1 = register_serial( &req )) < 0) {
+      printk( "Cannot register MFC III serial port: no free device\n" );
+      return -EBUSY;
+  }
+  lines[nr_mfc++] = line1;
+  info = &rs_table[line1];
+  info->sw = &mfc_ser_switch;
+  info->nr_uarts = nr_mfc;
+  info->board_base = dp;
+
+  req.line = -1; /* first free ttyS? device */
+  req.type = SER_MFC_III;
+  req.port = (int)&dp->pb;
+  if ((line2 = register_serial( &req )) < 0) {
+      printk( "Cannot register MFC III serial port: no free device\n" );
+	  unregister_serial( line1 );
+      return -EBUSY;
+  }
+  lines[nr_mfc++] = line2;
+  info = &rs_table[line2];
+  info->sw = &mfc_ser_switch;
+  info->nr_uarts = nr_mfc--;
+  info->board_base = dp;
+
+  if (nr_mfc < 4) {
+      request_irq(IRQ_AMIGA_EXTER, mfc_interrupt, 0,
+                  "Multiface III serial", mfc_interrupt);
+      request_irq(IRQ_AMIGA_VERTB, mfc_vbl_inter, 0,
+                  "Multiface III serial VBL", mfc_vbl_inter);
+  }
+
+  dp->ir.imr = 0; /* turn off all interrupts */
+  imask[nr_mfc/2] = 0;
+  dummy = dp->ir.isr; /* clear peding interrupts */
+  dummy = dp->ipcr_acr.ipcr;
+  dp->stop_ropc.ropc = 255; /* all outputs off */
+  dp->ipr_opcr.opcr = 0; /* change for serial-master */
+  dp->pa.cr = CR_RX_OFF; /* disable receiver */
+  dp->pb.cr = CR_RX_OFF;
+  dp->pa.cr = CR_TX_OFF; /* disable transmitter */
+  dp->pb.cr = CR_TX_OFF;
+  dp->pa.cr = CR_RESET_RX;
+  dp->pb.cr = CR_RESET_RX;
+  dp->pa.cr = CR_RESET_TX;
+  dp->pb.cr = CR_RESET_TX;
+  dp->pa.cr = CR_RESET_ERR;
+  dp->pb.cr = CR_RESET_ERR;
+  dp->pa.cr = CR_RESET_BREAK;
+  dp->pb.cr = CR_RESET_BREAK;
+  dp->pa.cr = CR_STOP_BREAK;
+  dp->pb.cr = CR_STOP_BREAK;
+  dp->pa.cr = CR_RESET_MR;
+  dp->pb.cr = CR_RESET_MR;
+  /* set default 9600  8 N 1 */
+  dp->pa.mr.mr1 = MR1_8BITS|MR1_PARITY_NO|MR1_RxRTS_ON;
+  dp->pb.mr.mr1 = MR1_8BITS|MR1_PARITY_NO|MR1_RxRTS_ON;
+  dp->pa.mr.mr2 = MR2_1STOP;
+  dp->pb.mr.mr2 = MR2_1STOP;
+
+  dp->ipcr_acr.acr = 0xe0; /* BRG set 2,  timer 1X crystal */
+  acmask[nr_mfc/2] = 0xe0;
+  /* change the following baudrate stuff for MFC_II */
+  dp->pa.sr_csr.csr = 0x99; /* 9600 from BRG */
+  dp->pb.sr_csr.csr = 0xdd; /* from timer */
+  dp->ctu = 0;
+  dp->ctl = 24; /* values for 9600 */
+  dummy = dp->start_sopc.start; /* load timer with new values */
+  nr_mfc++;
+
+  zorro_config_board(i,1);
+}
+return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+return multiface_init();
+}
+
+void cleanup_module(void)
+{
+int i;
+struct duart *dp;
+
+for (i = 0; i < nr_mfc; i += 2) {
+  dp = rs_table[lines[i]].board_base;
+  dp->pa.cr = CR_RX_OFF | CR_TX_OFF; /* disable duart */
+  dp->pb.cr = CR_RX_OFF | CR_TX_OFF;
+  dp->ir.imr = 0;   /* turn off interrupts */
+  unregister_serial(lines[i]);
+  unregister_serial(lines[i+1]);
+  zorro_unconfig_board(board_index[i/2], 1);
+}
+free_irq(IRQ_AMIGA_EXTER, mfc_interrupt);
+free_irq(IRQ_AMIGA_VERTB, mfc_vbl_inter);
+}
+#endif
+
+
diff -urN pc-2.0.21/drivers/char/tiga.c linux-2.0.21/drivers/char/tiga.c
--- pc-2.0.21/drivers/char/tiga.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/char/tiga.c	Thu Sep 26 22:03:28 1996
@@ -0,0 +1,166 @@
+/* tiga.c
+ *
+ * Copyright (C) 1996 Jes Sorensen (jds@kom.auc.dk)
+ *
+ * Interface to TMS340x0 (TIGA) based graphic boards.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+#include <linux/mman.h>
+
+#include <asm/setup.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/zorro.h>
+#include <asm/amigahw.h>
+
+#define TIGA_MAJOR	63
+#define MAX_TIGA	2	/* Maximum number of Tiga boards */
+
+#define TIGA_RESOLVER	1
+#define TIGA_VIVID24	2
+#define TIGA_A2410	3
+
+struct TigaInfo {
+	int in_use;
+	void *iobase;		/* Base of the TMS340x0 I/O registers	*/
+	unsigned long iosize;	/* Size of I/O-register area.		*/
+	void *membase;		/*
+				 * Base of board-memory, used by boards
+				 * that share memory between the host and
+				 * the GSP (like the Vivid24).
+				 */
+	unsigned long memsize;	/* Size of host/GSP shared memory.	*/
+	int boardtype;
+};
+
+static struct TigaInfo tigainfo[MAX_TIGA];
+
+static int
+tiga_mmap(struct inode *inode, struct file *file, struct vm_area_struct * vma)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+
+	if ((minor >= MAX_TIGA) || (tigainfo[minor].iobase == NULL))
+		return -ENODEV;
+
+	if (vma->vm_offset & ~PAGE_MASK)
+		return -ENXIO;
+
+	if ((vma->vm_end - vma->vm_start) > tigainfo[minor].iosize)
+		return -EINVAL;
+
+	if(m68k_is040or060){
+		pgprot_val(vma->vm_page_prot) &= _CACHEMASK040;
+		pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE_S;
+	}
+
+	if(remap_page_range(vma->vm_start, ZTWO_VADDR(tigainfo[minor].iobase),
+			    vma->vm_end - vma->vm_start, vma->vm_page_prot))
+		return -EAGAIN;
+	vma->vm_inode = inode;
+	inode->i_count++;
+
+	return 0;
+}
+
+
+static int tiga_open(struct inode *inode, struct file *file)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+
+	if ((minor >= MAX_TIGA) || (tigainfo[minor].iobase == NULL))
+		return -ENODEV;
+
+	if (tigainfo[minor].in_use == 0){
+		tigainfo[minor].in_use = 1;
+		return 0;
+	}
+	else
+		return -EBUSY;
+}
+
+
+static void tiga_release(struct inode *inode, struct file *file)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+
+	tigainfo[minor].in_use = 0;
+	return;
+}
+
+
+static struct file_operations tiga_fops = 
+{
+	NULL,           /* lseek        */
+	NULL,           /* read         */
+	NULL,           /* write        */
+	NULL,           /* readdir      */
+	NULL,           /* select       */
+	NULL,           /* ioctl        */
+	tiga_mmap,      /* mmap         */
+	tiga_open,      /* open         */
+	tiga_release,   /* release      */
+	NULL            /* fsync        */
+};
+
+
+void gsp_init(void)
+{
+	int key, i;
+	struct ConfigDev *cd;
+
+	i = 0;
+
+	/*
+	 * Identify DMI Resolver boards.
+	 */
+	while ((i < MAX_TIGA) && (key = zorro_find(MANUF_APPLIED_MAGIC,
+						   PROD_DMI_RESOLVER, 0, 0))){
+		cd = zorro_get_board(key);
+		tigainfo[i].iobase = cd->cd_BoardAddr;
+		tigainfo[i].iosize = cd->cd_BoardSize;
+		tigainfo[i].membase = NULL;
+		tigainfo[i].memsize = 0;
+		tigainfo[i].in_use = 0;
+		tigainfo[i].boardtype = TIGA_RESOLVER;
+		printk("Configuring DMI Resolver at 0x%08x as /dev/tiga%i.\n",
+		       (unsigned) tigainfo[i].iobase, i);
+		i++;
+		zorro_config_board(key, 0);
+	}
+	/*
+	 * Identify Commodore ULOWELL A2410 boards.
+	 */
+	while ((i < MAX_TIGA) &&
+	       ((key = zorro_find(MANUF_UNIV_OF_LOWELL, PROD_A2410, 0, 0)) ||
+		(key = zorro_find(MANUF_CARDCO, PROD_CC_A2410, 0, 0)))){
+
+		cd = zorro_get_board(key);
+		tigainfo[i].iobase = cd->cd_BoardAddr;
+		tigainfo[i].iosize = cd->cd_BoardSize;
+		tigainfo[i].membase = NULL;
+		tigainfo[i].memsize = 0;
+		tigainfo[i].in_use = 0;
+		tigainfo[i].boardtype = TIGA_A2410;
+		printk("Configuring A2410 at 0x%08x as /dev/tiga%i.\n",
+		       (unsigned) tigainfo[i].iobase, i);
+		i++;
+		zorro_config_board(key, 0);
+	}
+	if (i > 0){
+		if (register_chrdev(TIGA_MAJOR, "tiga", &tiga_fops)){
+			printk("Unable to register TIGA device\n");
+			return;
+		}
+	}
+	/*
+	 * Clear the tiga-structures that are not being used.
+	 */
+	for (; i < MAX_TIGA; i++)
+		tigainfo[i].iobase = NULL;
+}
diff -urN pc-2.0.21/drivers/char/tty_io.c linux-2.0.21/drivers/char/tty_io.c
--- pc-2.0.21/drivers/char/tty_io.c	Sat Sep  7 17:09:14 1996
+++ linux-2.0.21/drivers/char/tty_io.c	Thu Sep 12 21:48:07 1996
@@ -1907,7 +1907,10 @@
 		panic("Couldn't register /dev/console driver\n");
 	
 	kbd_init();
-#ifdef CONFIG_SERIAL
+#if defined(CONFIG_SERIAL) || defined(CONFIG_ATARI_MFPSER) || \
+    defined(CONFIG_ATARI_SCC) || defined(CONFIG_ATARI_MIDI) || \
+    defined(CONFIG_AMIGA_BUILTIN_SERIAL) || defined(CONFIG_GVIOEXT) || \
+    defined(CONFIG_MULTIFACE_III_TTY) || defined(CONFIG_USERIAL)
 	rs_init();
 #endif
 #ifdef CONFIG_SCC
diff -urN pc-2.0.21/drivers/char/vc_screen.c linux-2.0.21/drivers/char/vc_screen.c
--- pc-2.0.21/drivers/char/vc_screen.c	Tue May  7 06:50:53 1996
+++ linux-2.0.21/drivers/char/vc_screen.c	Sat Aug 24 14:35:15 1996
@@ -30,7 +30,21 @@
 static inline int
 vcs_size(struct inode *inode)
 {
+#if !defined(__mc68000__)
 	int size = video_num_lines * video_num_columns;
+#else
+	unsigned int cons = MINOR(inode->i_rdev) & 127;
+	int size;
+
+	if (cons == 0)
+		cons = fg_console;
+	else
+		cons--;
+	if (!vc_cons_allocated(cons))
+		return -ENXIO;
+
+	size = get_video_num_lines(cons) * get_video_num_columns(cons);
+#endif
 	if (MINOR(inode->i_rdev) & 128)
 		size = 2*size + HEADER_SIZE;
 	return size;
@@ -94,8 +108,13 @@
 	} else {
 		if (p < HEADER_SIZE) {
 			char header[HEADER_SIZE];
+#if !defined(__mc68000__)
 			header[0] = (char) video_num_lines;
 			header[1] = (char) video_num_columns;
+#else
+			header[0] = (char) get_video_num_lines(cons);
+			header[1] = (char) get_video_num_columns(cons);
+#endif
 			getconsxy(cons, header+2);
 			while (p < HEADER_SIZE && count-- > 0)
 			    put_user(header[p++], buf++);
@@ -105,8 +124,14 @@
 		if ((p & 1) && count-- > 0)
 			put_user(scr_readw(org++) >> 8, buf++);
 		while (count > 1) {
+#if !defined(__mc68000__)
 			put_user(scr_readw(org++), (unsigned short *) buf);
 			buf += 2;
+#else
+			short word = scr_readw(org++);
+			put_user(word & 0xff, buf++);
+			put_user(word >> 8, buf++);
+#endif
 			count -= 2;
 		}
 		if (count > 0)
@@ -169,14 +194,25 @@
 			org++;
 		}
 		while (count > 1) {
+#if !defined(__mc68000__)
 			scr_writew(get_user((const unsigned short *) buf), org++);
 			buf += 2;
+#else
+			short word = get_user(buf++);
+			word |= get_user(buf++) << 8;
+			scr_writew(word, org++);
+#endif
 			count -= 2;
 		}
 		if (count > 0)
 			scr_writew((scr_readw(org) & 0xff00) |
 				   get_user((const unsigned char*)buf++), org);
 	}
+#ifdef __mc68000__
+	if (cons == fg_console)
+		/* Horribly inefficient if count < screen size.  */
+		update_screen(cons);
+#endif
 	written = buf - buf0;
 	file->f_pos += written;
 	return written;
diff -urN pc-2.0.21/drivers/char/vt.c linux-2.0.21/drivers/char/vt.c
--- pc-2.0.21/drivers/char/vt.c	Mon May 13 06:36:19 1996
+++ linux-2.0.21/drivers/char/vt.c	Sat Aug 24 14:35:17 1996
@@ -24,6 +24,10 @@
 #include <asm/io.h>
 #include <asm/segment.h>
 
+#if defined(__mc68000__) /* Needed because of mach_mksound */
+#include <asm/machdep.h>
+#endif
+
 #include "kbd_kern.h"
 #include "vt_kern.h"
 #include "diacr.h"
@@ -50,6 +54,11 @@
 
 struct vt_struct *vt_cons[MAX_NR_CONSOLES];
 
+/* Keyboard type: Default is KB_101, but can be set be machine
+ * specific code.
+ */
+unsigned char mach_keyboard_type = KB_101;
+
 #ifndef __alpha__
 asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on);
 #endif
@@ -147,6 +156,7 @@
  * We also return immediately, which is what was implied within the X
  * comments - KDMKTONE doesn't put the process to sleep.
  */
+#if !defined (__mc68000__)
 static void
 kd_nosound(unsigned long ignored)
 {
@@ -188,7 +198,20 @@
 }
 
 void (*kd_mksound)(unsigned int hz, unsigned int ticks) = _kd_mksound;
-	
+
+#else /* __mc68000__ */
+void dummy_mksound(unsigned int say, unsigned int nothing)
+{
+  /* We are real quiet in here ;-) */
+}
+
+/*
+ * Since this is machine-dependent, systems with a sound-device should
+ * change this value in arch/m68k/{amiga,atari,x}/config.c.
+ */
+void (*kd_mksound)(unsigned int hz, unsigned int ticks) = dummy_mksound;
+#endif
+
 /*
  * We handle the console-specific ioctl's here.  We allow the
  * capability to modify any console, not just the fg_console. 
@@ -248,9 +271,71 @@
 		 */
 		i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
 		if (!i)
-			put_user(KB_101, (char *) arg);
+			put_user(mach_keyboard_type, (char *) arg);
 		return i;
 
+#ifdef __mc68000__
+	/* Linux/68k interface to the hardware clock */
+		
+	case KDGHWCLK:
+	{
+		struct hwclk_time time;
+
+		i = verify_area( VERIFY_WRITE, (void *)arg,
+						 sizeof(struct hwclk_time) );
+		if (i)
+			return( i );
+		if (!mach_hwclk)
+			return( -EINVAL );
+		if ((i = mach_hwclk( 0, &time )))
+			return( i );
+		memcpy_tofs( (void *)arg, &time, sizeof(struct hwclk_time) );
+		return( 0 );
+	}
+
+	case KDSHWCLK:
+	{
+		struct hwclk_time time;
+
+		if (!suser()) return( -EPERM );
+		i = verify_area( VERIFY_READ, (void *)arg,
+						 sizeof(struct hwclk_time) );
+		if (i) return( i );
+		memcpy_fromfs( &time, (void *)arg, sizeof(struct hwclk_time) );
+
+		/* simple checking... (mach_hwclk may do more) */
+		if (!mach_hwclk ||
+			time.sec  > 59 ||
+			time.min  > 59 ||
+			time.hour > 23 ||
+			time.day  < 1  || time.day  > 31 ||
+			time.mon  > 11 ||
+			time.wday < -1 || time.wday > 6  ||
+			time.year < 70)
+			return( -EINVAL );
+		return( mach_hwclk( 1, &time ) );
+	}
+
+	/* Linux/68k interface for setting the keyboard delay/repeat rate */
+		
+	case KDKBDREP:
+	{
+		struct kbd_repeat kbrep;
+		
+		if (!mach_kbdrate) return( -EINVAL );
+		if (!suser()) return( -EPERM );
+
+		i = verify_area( VERIFY_WRITE, (void *)arg,
+						 sizeof(struct kbd_repeat) );
+		if (i) return( i );
+
+		memcpy_fromfs( &kbrep, (void *)arg, sizeof(struct kbd_repeat) );
+		if ((i = mach_kbdrate( &kbrep ))) return( i );
+		memcpy_tofs( (void *)arg, &kbrep, sizeof(struct kbd_repeat) );
+		return( 0 );
+	}
+#endif
+
 #ifndef __alpha__
 		/*
 		 * These cannot be implemented on any machine that implements
@@ -472,9 +557,12 @@
 		    if (kbd->kbdmode != VC_UNICODE)
 			return -EINVAL;
 
+#ifndef __mc68000__
 		/* assignment to entry 0 only tests validity of args */
+		/* ++Geert: on m68k 0x00 is a valid keycode */
 		if (!i)
 			return 0;
+#endif /* __mc68000__ */
 
 		if (!(key_map = key_maps[s])) {
 			int j;
diff -urN pc-2.0.21/drivers/char/vt_kern.h linux-2.0.21/drivers/char/vt_kern.h
--- pc-2.0.21/drivers/char/vt_kern.h	Sun Mar 24 12:35:08 1996
+++ linux-2.0.21/drivers/char/vt_kern.h	Mon Sep 16 23:53:47 1996
@@ -30,10 +30,12 @@
 	struct wait_queue *paste_wait;
 } *vt_cons[MAX_NR_CONSOLES];
 
-void (*kd_mksound)(unsigned int hz, unsigned int ticks);
+extern void (*kd_mksound)(unsigned int hz, unsigned int ticks);
 int vc_allocate(unsigned int console);
 int vc_cons_allocated(unsigned int console);
 int vc_resize(unsigned long lines, unsigned long cols);
+void vc_resize_con(unsigned long lines, unsigned long cols,
+		   unsigned int currcons);
 void vc_disallocate(unsigned int console);
 
 #endif /* _VT_KERN_H */
diff -urN pc-2.0.21/drivers/net/Makefile linux-2.0.21/drivers/net/Makefile
--- pc-2.0.21/drivers/net/Makefile	Mon May  6 11:44:31 1996
+++ linux-2.0.21/drivers/net/Makefile	Sat Aug 24 14:36:12 1996
@@ -458,6 +458,22 @@
   endif
 endif
 
+ifeq ($(CONFIG_ATARI_BIONET),y)
+L_OBJS += atari_bionet.o
+else
+  ifeq ($(CONFIG_ATARI_BIONET),m)
+  M_OBJS += atari_bionet.o
+  endif
+endif
+
+ifeq ($(CONFIG_ATARI_PAMSNET),y)
+L_OBJS += atari_pamsnet.o
+else
+  ifeq ($(CONFIG_ATARI_PAMSNET),m)
+  M_OBJS += atari_pamsnet.o
+  endif
+endif
+
 ifeq ($(CONFIG_A2065),y)
 L_OBJS += a2065.o
 else
diff -urN pc-2.0.21/drivers/net/a2065.c linux-2.0.21/drivers/net/a2065.c
--- pc-2.0.21/drivers/net/a2065.c	Mon May 20 06:54:28 1996
+++ linux-2.0.21/drivers/net/a2065.c	Sat Aug 24 14:36:21 1996
@@ -51,11 +51,11 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
+#include <asm/setup.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 
-#include <asm/bootinfo.h>
 #include <asm/amigaints.h>
 #include <asm/amigahw.h>
 #include <asm/zorro.h>
@@ -121,7 +121,7 @@
 static void a2065_init_ring(struct device *dev);
 static int a2065_start_xmit(struct sk_buff *skb, struct device *dev);
 static int a2065_rx(struct device *dev);
-static void a2065_interrupt(int irq, struct pt_regs *fp, void *data);
+static void a2065_interrupt(int irq, void *data, struct pt_regs *fp);
 static int a2065_close(struct device *dev);
 static struct enet_statistics *a2065_get_stats(struct device *dev);
 static void set_multicast_list(struct device *dev);
@@ -136,6 +136,7 @@
 	struct a2065_private *priv;
 
 	if ((key1 = zorro_find(MANUF_COMMODORE, PROD_A2065, 0, 0)) ||
+	    (key1 = zorro_find(MANUF_COMMODORE, PROD_A2065_2, 0, 0)) ||
 	    (key2 = zorro_find(MANUF_AMERISTAR, PROD_AMERISTAR2065, 0, 0))) {
 		cd = zorro_get_board(key1 ? key1 : key2);
 		if ((board = (u_long)cd->cd_BoardAddr)) {
@@ -236,7 +237,7 @@
 
 
 	/* Install the Interrupt handler */
-	if (!add_isr(IRQ_AMIGA_PORTS, a2065_interrupt, 0, dev, "a2065 Ethernet"))
+	if (request_irq(IRQ_AMIGA_PORTS, a2065_interrupt, 0, "a2065 Ethernet", dev))
 		return(-EAGAIN);
 
 	/* Make the LANCE read the Init Block */
@@ -324,7 +325,7 @@
 	/* We stop the LANCE here - it occasionally polls memory if we don't */
 	board->Lance.RDP = STOP;
 
-	remove_isr(IRQ_AMIGA_PORTS, a2065_interrupt, dev);
+	free_irq(IRQ_AMIGA_PORTS, dev);
 
 	MOD_DEC_USE_COUNT;
 
@@ -332,7 +333,7 @@
 }
 
 
-static void a2065_interrupt(int irq, struct pt_regs *fp, void *data)
+static void a2065_interrupt(int irq, void *data, struct pt_regs *fp)
 {
 	struct device *dev = (struct device *)data;
 	struct a2065_private *priv;
diff -urN pc-2.0.21/drivers/net/ariadne.c linux-2.0.21/drivers/net/ariadne.c
--- pc-2.0.21/drivers/net/ariadne.c	Mon May 20 06:54:28 1996
+++ linux-2.0.21/drivers/net/ariadne.c	Sat Aug 24 14:36:25 1996
@@ -50,11 +50,11 @@
 #include <linux/interrupt.h>
 #include <linux/skbuff.h>
 
+#include <asm/setup.h>
 #include <asm/bitops.h>
 #include <asm/amigaints.h>
 #include <asm/amigahw.h>
 #include <asm/zorro.h>
-#include <asm/bootinfo.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 
@@ -127,7 +127,7 @@
 static void ariadne_init_ring(struct device *dev);
 static int ariadne_start_xmit(struct sk_buff *skb, struct device *dev);
 static int ariadne_rx(struct device *dev);
-static void ariadne_interrupt(int irq, struct pt_regs *fp, void *data);
+static void ariadne_interrupt(int irq, void *data, struct pt_regs *fp);
 static int ariadne_close(struct device *dev);
 static struct enet_statistics *ariadne_get_stats(struct device *dev);
 #ifdef HAVE_MULTICAST
@@ -291,8 +291,8 @@
     dev->interrupt = 0;
     dev->start = 1;
 
-    if (!add_isr(IRQ_AMIGA_PORTS, ariadne_interrupt, 0, dev,
-    		 "Ariadne Ethernet"))
+    if (request_irq(IRQ_AMIGA_PORTS, ariadne_interrupt, 0,
+                    "Ariadne Ethernet", dev))
 	return(-EAGAIN);
 
     board->Lance.RAP = CSR0;	/* PCnet-ISA Controller Status */
@@ -373,7 +373,7 @@
     /* We stop the LANCE here -- it occasionally polls memory if we don't. */
     board->Lance.RDP = STOP;
 
-    remove_isr(IRQ_AMIGA_PORTS, ariadne_interrupt, dev);
+    free_irq(IRQ_AMIGA_PORTS, dev);
 
     MOD_DEC_USE_COUNT;
 
@@ -381,7 +381,7 @@
 }
 
 
-static void ariadne_interrupt(int irq, struct pt_regs *fp, void *data)
+static void ariadne_interrupt(int irq, void *data, struct pt_regs *fp)
 {
     struct device *dev = (struct device *)data;
     struct ariadne_private *priv;
diff -urN pc-2.0.21/drivers/net/atari_bionet.c linux-2.0.21/drivers/net/atari_bionet.c
--- pc-2.0.21/drivers/net/atari_bionet.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/net/atari_bionet.c	Sat Aug 24 14:36:27 1996
@@ -0,0 +1,649 @@
+/* bionet.c     BioNet-100 device driver for linux68k.
+ *
+ * Version:	@(#)bionet.c	1.0	02/06/96
+ *
+ * Author:	Hartmut Laue <laue@ifk-mp.uni-kiel.de> 
+ * and		Torsten Narjes <narjes@ifk-mp.uni-kiel.de>
+ *
+ * Little adaptions for integration into pl7 by Roman Hodek
+ *
+	What is it ?
+	------------
+	This driver controls the BIONET-100 LAN-Adapter which connects
+	an ATARI ST/TT via the ACSI-port to an Ethernet-based network.
+
+	This version can be compiled as a loadable module (See the
+	compile command at the bottom of this file).
+	At load time, you can optionally set the debugging level and the
+	fastest response time on the command line of 'insmod'.
+
+	'bionet_debug'
+		controls the amount of diagnostic messages:
+	  0  : no messages
+	  >0 : see code for meaning of printed messages
+
+	'bionet_min_poll_time' (always >=1)
+		gives the time (in jiffies) between polls. Low values
+		increase the system load (beware!)
+
+	When loaded, a net device with the name 'bio0' becomes available,
+	which can be controlled with the usual 'ifconfig' command.
+
+	It is possible to compile this driver into the kernel like other
+	(net) drivers. For this purpose, some source files (e.g. config-files
+	makefiles, Space.c) must be changed accordingly. (You may refer to
+	other drivers how to do it.) In this case, the device will be detected
+	at boot time and (probably) appear as 'eth0'.
+
+	This code is based on several sources:
+	- The driver code for a parallel port ethernet adapter by
+	  Donald Becker (see file 'atp.c' from the PC linux distribution)
+	- The ACSI code by Roman Hodek for the ATARI-ACSI harddisk support
+	  and DMA handling.
+	- Very limited information about moving packets in and out of the
+	  BIONET-adapter from the TCP package for TOS by BioData GmbH.
+
+	Theory of Operation
+	-------------------
+	Because the ATARI DMA port is usually shared between several
+	devices (eg. harddisk, floppy) we cannot block the ACSI bus
+	while waiting for interrupts. Therefore we use a polling mechanism
+	to fetch packets from the adapter. For the same reason, we send
+	packets without checking that the previous packet has been sent to
+	the LAN. We rely on the higher levels of the networking code to detect
+	missing packets and resend them.
+
+	Before we access the ATARI DMA controller, we check if another
+	process is using the DMA. If not, we lock the DMA, perform one or
+	more packet transfers and unlock the DMA before returning.
+	We do not use 'stdma_lock' unconditionally because it is unclear
+	if the networking code can be set to sleep, which will happen if
+	another (possibly slow) device is using the DMA controller.
+
+	The polling is done via timer interrupts which periodically
+	'simulate' an interrupt from the Ethernet adapter. The time (in jiffies)
+	between polls varies depending on an estimate of the net activity.
+	The allowed range is given by the variable 'bionet_min_poll_time'
+	for the lower (fastest) limit and the constant 'MAX_POLL_TIME'
+	for the higher (slowest) limit.
+
+	Whenever a packet arrives, we switch to fastest response by setting
+	the polling time to its lowest limit. If the following poll fails,
+	because no packets have arrived, we increase the time for the next
+	poll. When the net activity is low, the polling time effectively
+	stays at its maximum value, resulting in the lowest load for the
+	machine.
+ */
+
+#define MAX_POLL_TIME	10
+
+static char *version =
+	"bionet.c:v1.0 06-feb-96 (c) Hartmut Laue.\n";
+
+#ifdef MODULE
+#include <linux/module.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/setup.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atari_acsi.h>
+#include <asm/atari_stdma.h>
+
+  
+extern struct device *init_etherdev(struct device *dev, int sizeof_private);
+
+/* use 0 for production, 1 for verification, >2 for debug
+ */
+#ifndef NET_DEBUG
+#define NET_DEBUG 0
+#endif
+/*
+ * Global variable 'bionet_debug'. Can be set at load time by 'insmod'
+ */
+unsigned int bionet_debug = NET_DEBUG;
+static unsigned int bionet_min_poll_time = 2;
+
+
+/* Information that need to be kept for each board.
+ */
+struct net_local {
+	struct enet_statistics stats;
+	long open_time;			/* for debugging */
+	int  poll_time;			/* polling time varies with net load */
+};
+
+static struct nic_pkt_s {		/* packet format */
+	unsigned char	status;
+	unsigned char	dummy;
+	unsigned char	l_lo, l_hi;
+	unsigned char	buffer[3000];
+} *nic_packet;
+unsigned char *phys_nic_packet;
+
+/* Index to functions, as function prototypes.
+ */
+extern int bionet_probe(struct device *dev);
+
+static int bionet_open(struct device *dev);
+static int bionet_send_packet(struct sk_buff *skb, struct device *dev);
+static void bionet_poll_rx(struct device *);
+static int bionet_close(struct device *dev);
+static struct enet_statistics *net_get_stats(struct device *dev);
+static void bionet_tick(unsigned long);
+
+static struct timer_list bionet_timer = { NULL, NULL, 0, 0, bionet_tick };
+
+#define STRAM_ADDR(a)	(((a) & 0xff000000) == 0)
+
+/* The following routines access the ethernet board connected to the
+ * ACSI port via the st_dma chip.
+ */
+#define NODE_ADR 0x60
+
+#define C_READ 8
+#define C_WRITE 0x0a
+#define C_GETEA 0x0f
+#define C_SETCR 0x0e
+
+static int
+sendcmd(unsigned int a0, unsigned int mod, unsigned int cmd) {
+	unsigned int c;
+
+	dma_wd.dma_mode_status = (mod | ((a0) ? 2 : 0) | 0x88);
+	dma_wd.fdc_acces_seccount = cmd; 
+	dma_wd.dma_mode_status = (mod | 0x8a);
+
+	if( !acsi_wait_for_IRQ(HZ/2) )	/* wait for cmd ack */
+		return -1;		/* timeout */
+
+	c = dma_wd.fdc_acces_seccount;
+	return (c & 0xff);
+}
+
+
+static void
+set_status(int cr) {
+	sendcmd(0,0x100,NODE_ADR | C_SETCR);    /* CMD: SET CR */
+	sendcmd(1,0x100,cr);
+
+	dma_wd.dma_mode_status = 0x80;
+}
+
+static int
+get_status(unsigned char *adr) {
+	int i,c;
+
+	DISABLE_IRQ();
+	c = sendcmd(0,0x00,NODE_ADR | C_GETEA);  /* CMD: GET ETH ADR*/
+	if( c < 0 ) goto gsend;
+
+	/* now read status bytes */
+
+	for (i=0; i<6; i++) {
+		dma_wd.fdc_acces_seccount = 0;	/* request next byte */
+
+    		if( !acsi_wait_for_IRQ(HZ/2) ) {	/* wait for cmd ack */
+			c = -1;
+			goto gsend;		/* timeout */
+		}
+		c = dma_wd.fdc_acces_seccount;
+		*adr++ = (unsigned char)c;
+	}
+	c = 1;
+gsend:
+  	dma_wd.dma_mode_status = 0x80;
+	return c;
+}
+
+static void
+bionet_intr(int irq, void *data, struct pt_regs *fp) {
+	return;
+}
+
+
+static int
+get_frame(unsigned long paddr, int odd) {
+	int c;
+	unsigned long flags;
+
+	DISABLE_IRQ();
+	save_flags(flags);
+	cli();
+
+	dma_wd.dma_mode_status		= 0x9a;
+	dma_wd.dma_mode_status		= 0x19a;
+	dma_wd.dma_mode_status		= 0x9a;
+	dma_wd.fdc_acces_seccount	= 0x05;		/* sector count */
+	dma_wd.dma_lo			= (unsigned char)paddr;
+	paddr >>= 8;
+	dma_wd.dma_md			= (unsigned char)paddr;
+	paddr >>= 8;
+	dma_wd.dma_hi			= (unsigned char)paddr;
+	restore_flags(flags);
+
+	c = sendcmd(0,0x00,NODE_ADR | C_READ);	/* CMD: READ */
+	if( c < 128 ) goto rend;
+
+	/* now read block */
+
+	c = sendcmd(1,0x00,odd);	/* odd flag for address shift */
+	dma_wd.dma_mode_status	= 0x0a;
+
+	if( !acsi_wait_for_IRQ(100) ) {	/* wait for DMA to complete */
+		c = -1;
+		goto rend;
+	}
+	dma_wd.dma_mode_status	= 0x8a;
+	dma_wd.dma_mode_status	= 0x18a;
+	dma_wd.dma_mode_status	= 0x8a;
+	c = dma_wd.fdc_acces_seccount;
+
+	dma_wd.dma_mode_status	= 0x88;
+	c = dma_wd.fdc_acces_seccount;
+	c = 1;
+  
+rend:
+	dma_wd.dma_mode_status	= 0x80;
+	udelay(40);
+	acsi_wait_for_noIRQ(20);
+	return c;
+}
+
+
+static int
+hardware_send_packet(unsigned long paddr, int cnt) {
+	unsigned int c;
+	unsigned long flags;
+
+	DISABLE_IRQ();
+	save_flags(flags);
+	cli();
+
+	dma_wd.dma_mode_status	= 0x19a;
+	dma_wd.dma_mode_status	= 0x9a;
+	dma_wd.dma_mode_status	= 0x19a;
+	dma_wd.dma_lo		= (unsigned char)paddr;
+	paddr >>= 8;
+	dma_wd.dma_md		= (unsigned char)paddr;
+	paddr >>= 8;
+	dma_wd.dma_hi		= (unsigned char)paddr;
+
+	dma_wd.fdc_acces_seccount	= 0xaa;		/* sector count */
+	restore_flags(flags);
+
+	c = sendcmd(0,0x100,NODE_ADR | C_WRITE);	/* CMD: WRITE */
+	c = sendcmd(1,0x100,cnt&0xff);
+	c = sendcmd(1,0x100,cnt>>8);
+
+	/* now write block */
+
+	dma_wd.dma_mode_status	= 0x10a;	/* DMA enable */
+	if( !acsi_wait_for_IRQ(100) )		/* wait for DMA to complete */
+		goto end;
+
+	dma_wd.dma_mode_status	= 0x19a;	/* DMA disable ! */
+	c = dma_wd.fdc_acces_seccount;
+
+end:
+	c = sendcmd(1,0x100,0);
+	c = sendcmd(1,0x100,0);
+
+	dma_wd.dma_mode_status	= 0x180;
+	udelay(40);
+	acsi_wait_for_noIRQ(20);
+	return( c & 0x02);
+}
+
+
+/* Check for a network adaptor of this type, and return '0' if one exists.
+ */
+int
+bionet_probe(struct device *dev) {
+	unsigned char station_addr[6];
+	static unsigned version_printed = 0;
+	static int no_more_found = 0; /* avoid "Probing for..." printed 4 times */
+	int i;
+
+	if (!MACH_IS_ATARI || no_more_found)
+		return ENODEV;
+	
+	printk("Probing for BioNet 100 Adapter...\n");
+
+	stdma_lock(bionet_intr, NULL);
+	i = get_status(station_addr);	/* Read the station address PROM.  */
+	ENABLE_IRQ();
+	stdma_release();
+
+	/* Check the first three octets of the S.A. for the manufactor's code.
+	 */ 
+
+	if( i < 0
+	||  station_addr[0] != 'B'
+	||  station_addr[1] != 'I'
+	||  station_addr[2] != 'O' ) {
+		no_more_found = 1;
+		printk( "No BioNet 100 found.\n" );
+		return ENODEV;
+	}
+
+
+	if (dev == NULL)
+		return ENODEV;
+	if (bionet_debug > 0 && version_printed++ == 0)
+		printk(version);
+
+	printk("%s: %s found, eth-addr: %02x-%02x-%02x:%02x-%02x-%02x.\n",
+		dev->name, "BioNet 100",
+		station_addr[0], station_addr[1], station_addr[2],
+		station_addr[3], station_addr[4], station_addr[5]);
+
+	/* Initialize the device structure. */
+
+	nic_packet = (struct nic_pkt_s *)acsi_buffer;
+	phys_nic_packet = (unsigned char *)phys_acsi_buffer;
+	if (bionet_debug > 0) {
+		printk("nic_packet at 0x%p, phys at 0x%p\n",
+			nic_packet, phys_nic_packet );
+	}
+
+	if (dev->priv == NULL)
+		dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+	memset(dev->priv, 0, sizeof(struct net_local));
+
+	dev->open		= bionet_open;
+	dev->stop		= bionet_close;
+	dev->hard_start_xmit	= bionet_send_packet;
+	dev->get_stats		= net_get_stats;
+
+	/* Fill in the fields of the device structure with ethernet-generic
+	 * values. This should be in a common file instead of per-driver.
+	 */
+
+	for (i = 0; i < ETH_ALEN; i++) {
+#if 0
+		dev->broadcast[i] = 0xff;
+#endif
+		dev->dev_addr[i]  = station_addr[i];
+	}
+	ether_setup(dev);
+	return 0;
+}
+
+/* Open/initialize the board.  This is called (in the current kernel)
+   sometime after booting when the 'ifconfig' program is run.
+
+   This routine should set everything up anew at each open, even
+   registers that "should" only need to be set once at boot, so that
+   there is non-reboot way to recover if something goes wrong.
+ */
+static int
+bionet_open(struct device *dev) {
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	if (bionet_debug > 0)
+		printk("bionet_open\n");
+	stdma_lock(bionet_intr, NULL);
+
+	/* Reset the hardware here.
+	 */
+	set_status(4);
+	lp->open_time = 0;	/*jiffies*/
+	lp->poll_time = MAX_POLL_TIME;
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	stdma_release();
+	bionet_timer.data = (long)dev;
+	bionet_timer.expires = jiffies + lp->poll_time;
+	add_timer(&bionet_timer);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int
+bionet_send_packet(struct sk_buff *skb, struct device *dev) {
+	struct net_local *lp = (struct net_local *)dev->priv;
+	unsigned long flags;
+
+	/* If some higher layer thinks we've missed an tx-done interrupt we
+	 * are passed NULL. Caution: dev_tint() handles the cli()/sti() itself.
+	 */
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+	/* Block a timer-based transmit from overlapping.  This could better be
+	 * done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+	 */
+	save_flags(flags);
+	cli();
+
+	if (stdma_islocked()) {
+		restore_flags(flags);
+		lp->stats.tx_errors++;
+	}
+	else {
+		int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+		unsigned long buf = VTOP(skb->data);
+		int stat;
+
+		stdma_lock(bionet_intr, NULL);
+		restore_flags(flags);
+		if( !STRAM_ADDR(buf+length-1) ) {
+			memcpy(nic_packet->buffer, skb->data, length);
+			buf = (unsigned long)&((struct nic_pkt_s *)phys_nic_packet)->buffer;
+		}
+
+		dma_cache_maintenance(buf, length, 1);
+
+		stat = hardware_send_packet(buf, length);
+		ENABLE_IRQ();
+		stdma_release();
+
+		dev->trans_start = jiffies;
+		dev->tbusy	 = 0;
+	}
+	dev_kfree_skb(skb, FREE_WRITE);
+
+	/* You might need to clean up and record Tx statistics here.
+	 */
+	lp->stats.tx_packets++;
+	return 0;
+}
+
+/* We have a good packet(s), get it/them out of the buffers.
+ */
+static void
+bionet_poll_rx(struct device *dev) {
+	struct net_local *lp = (struct net_local *)dev->priv;
+	int boguscount = 10;
+	int pkt_len, status;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	/* ++roman: Take care at locking the ST-DMA... This must be done with ints
+	 * off, since otherwise an int could slip in between the question and the
+	 * locking itself, and then we'd go to sleep... And locking itself is
+	 * necessary to keep the floppy_change timer from working with ST-DMA
+	 * registers. */
+	if (stdma_islocked()) {
+		restore_flags(flags);
+		return;
+	}
+	stdma_lock(bionet_intr, NULL);
+	DISABLE_IRQ();
+	restore_flags(flags);
+
+	if( lp->poll_time < MAX_POLL_TIME ) lp->poll_time++;
+
+	while(boguscount--) {
+		status = get_frame((unsigned long)phys_nic_packet, 0);
+	  
+		if( status != 1 ) break;
+
+		/* Good packet... */
+
+		dma_cache_maintenance((unsigned long)phys_nic_packet, 1520, 0);
+
+		pkt_len = (nic_packet->l_hi << 8) | nic_packet->l_lo;
+
+		lp->poll_time = bionet_min_poll_time;    /* fast poll */
+		if( pkt_len >= 60 && pkt_len <= 1514 ) {
+
+			/* Malloc up new buffer.
+			 */
+			struct sk_buff *skb = alloc_skb(pkt_len, GFP_ATOMIC);
+			if (skb == NULL) {
+				printk("%s: Memory squeeze, dropping packet.\n",
+					dev->name);
+				lp->stats.rx_dropped++;
+				break;
+			}
+			skb->len = pkt_len;
+			skb->dev = dev;
+
+			/* 'skb->data' points to the start of sk_buff data area.
+			 */
+			memcpy(skb->data, nic_packet->buffer, pkt_len);
+			netif_rx(skb);
+			lp->stats.rx_packets++;
+		}
+	}
+
+	/* If any worth-while packets have been received, dev_rint()
+	   has done a mark_bh(INET_BH) for us and will work on them
+	   when we get to the bottom-half routine.
+	 */
+
+	stdma_release();
+	ENABLE_IRQ();
+	return;
+}
+
+/* bionet_tick: called by bionet_timer. Reads packets from the adapter,
+ * passes them to the higher layers and restarts the timer.
+ */
+static void
+bionet_tick(unsigned long data) {
+	struct device	 *dev = (struct device *)data;
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	if( bionet_debug > 0 && (lp->open_time++ & 7) == 8 )
+		printk("bionet_tick: %ld\n", lp->open_time);
+
+	if( !stdma_islocked() ) bionet_poll_rx(dev);
+
+	bionet_timer.expires = jiffies + lp->poll_time;
+	add_timer(&bionet_timer);
+}
+
+/* The inverse routine to bionet_open().
+ */
+static int
+bionet_close(struct device *dev) {
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	if (bionet_debug > 0)
+		printk("bionet_close, open_time=%ld\n", lp->open_time);
+	del_timer(&bionet_timer);
+	stdma_lock(bionet_intr, NULL);
+
+	set_status(0);
+	lp->open_time = 0;
+
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	stdma_release();
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* Get the current statistics.
+   This may be called with the card open or closed.
+ */
+static struct enet_statistics *
+net_get_stats(struct device *dev) {
+	struct net_local *lp = (struct net_local *)dev->priv;
+	return &lp->stats;
+}
+
+
+#ifdef MODULE
+
+#include <linux/version.h>
+
+/* We should include the kernel identification string in the module.
+ */
+static char kernel_version[] = UTS_RELEASE;
+
+#undef	NEXT_DEV
+#define NEXT_DEV	(&bio_dev)
+
+static struct device bio_dev =  
+	{
+		"        ",	/* filled in by register_netdev() */
+		0, 0, 0, 0,	/* memory */
+		0, 0,		/* base, irq */
+		0, 0, 0, NEXT_DEV, bionet_probe,
+	};
+
+int
+init_module(void) {
+	int err;
+	
+	if ((err = register_netdev(&bio_dev))) {
+		if (err == -EEXIST)  {
+			printk("BIONET: devices already present. Module not loaded.\n");
+		}
+		return err;
+	}
+	return 0;
+}  
+
+void
+cleanup_module(void) {
+	unregister_netdev(&bio_dev);
+}
+                      
+#endif /* MODULE */
+
+/* Local variables:
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include
+	-b m68k-linuxaout -Wall -Wstrict-prototypes -O2
+	-fomit-frame-pointer -pipe -DMODULE -I../../net/inet -c bionet.c"
+ *  version-control: t
+ *  kept-new-versions: 5
+ *  tab-width: 8
+ * End:
+ */
diff -urN pc-2.0.21/drivers/net/atari_pamsnet.c linux-2.0.21/drivers/net/atari_pamsnet.c
--- pc-2.0.21/drivers/net/atari_pamsnet.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/net/atari_pamsnet.c	Sat Aug 24 14:36:27 1996
@@ -0,0 +1,921 @@
+/* atari_pamsnet.c     PAMsNet device driver for linux68k.
+ *
+ * Version:	@(#)PAMsNet.c	0.2ß	03/31/96
+ *
+ * Author:  Torsten Lang <Torsten.Lang@ap.physik.uni-giessen.de> 
+ *                       <Torsten.Lang@jung.de>
+ *
+ * This driver is based on my driver PAMSDMA.c for MiNT-Net and
+ * on the driver bionet.c written by
+ *          Hartmut Laue <laue@ifk-mp.uni-kiel.de> 
+ * and      Torsten Narjes <narjes@ifk-mp.uni-kiel.de>
+ *
+ * Little adaptions for integration into pl7 by Roman Hodek
+ *
+	What is it ?
+	------------
+	This driver controls the PAMsNet LAN-Adapter which connects
+	an ATARI ST/TT via the ACSI-port to an Ethernet-based network.
+
+	This version can be compiled as a loadable module (See the
+	compile command at the bottom of this file).
+	At load time, you can optionally set the debugging level and the
+	fastest response time on the command line of 'insmod'.
+
+	'pamsnet_debug'
+		controls the amount of diagnostic messages:
+	  0  : no messages
+	  >0 : see code for meaning of printed messages
+
+	'pamsnet_min_poll_time' (always >=1)
+		gives the time (in jiffies) between polls. Low values
+		increase the system load (beware!)
+
+	When loaded, a net device with the name 'eth?' becomes available,
+	which can be controlled with the usual 'ifconfig' command.
+
+	It is possible to compile this driver into the kernel like other
+	(net) drivers. For this purpose, some source files (e.g. config-files
+	makefiles, Space.c) must be changed accordingly. (You may refer to
+	other drivers how to do it.) In this case, the device will be detected
+	at boot time and (probably) appear as 'eth0'.
+
+	Theory of Operation
+	-------------------
+	Because the ATARI DMA port is usually shared between several
+	devices (eg. harddisk, floppy) we cannot block the ACSI bus
+	while waiting for interrupts. Therefore we use a polling mechanism
+	to fetch packets from the adapter. For the same reason, we send
+	packets without checking that the previous packet has been sent to
+	the LAN. We rely on the higher levels of the networking code to detect
+	missing packets and resend them.
+
+	Before we access the ATARI DMA controller, we check if another
+	process is using the DMA. If not, we lock the DMA, perform one or
+	more packet transfers and unlock the DMA before returning.
+	We do not use 'stdma_lock' unconditionally because it is unclear
+	if the networking code can be set to sleep, which will happen if
+	another (possibly slow) device is using the DMA controller.
+
+	The polling is done via timer interrupts which periodically
+	'simulate' an interrupt from the Ethernet adapter. The time (in jiffies)
+	between polls varies depending on an estimate of the net activity.
+	The allowed range is given by the variable 'bionet_min_poll_time'
+	for the lower (fastest) limit and the constant 'MAX_POLL_TIME'
+	for the higher (slowest) limit.
+
+	Whenever a packet arrives, we switch to fastest response by setting
+	the polling time to its lowest limit. If the following poll fails,
+	because no packets have arrived, we increase the time for the next
+	poll. When the net activity is low, the polling time effectively
+	stays at its maximum value, resulting in the lowest load for the
+	machine.
+ */
+
+#define MAX_POLL_TIME	10
+
+static char *version =
+	"pamsnet.c:v0.2beta 30-mar-96 (c) Torsten Lang.\n";
+
+#ifdef MODULE
+#include <linux/module.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atari_stdma.h>
+#include <asm/atari_acsi.h>
+
+#include <linux/delay.h>
+#include <linux/timer.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#undef READ
+#undef WRITE
+  
+extern struct device *init_etherdev(struct device *dev, int sizeof_private);
+
+/* use 0 for production, 1 for verification, >2 for debug
+ */
+#ifndef NET_DEBUG
+#define NET_DEBUG 0
+#endif
+/*
+ * Global variable 'pamsnet_debug'. Can be set at load time by 'insmod'
+ */
+unsigned int pamsnet_debug = NET_DEBUG;
+static unsigned int pamsnet_min_poll_time = 2;
+
+
+/* Information that need to be kept for each board.
+ */
+struct net_local {
+	struct enet_statistics stats;
+	long open_time;			/* for debugging */
+	int  poll_time;			/* polling time varies with net load */
+};
+
+static struct nic_pkt_s {		/* packet format */
+	unsigned char	buffer[2048];
+} *nic_packet = 0;
+unsigned char *phys_nic_packet;
+
+typedef unsigned char HADDR[6]; /* 6-byte hardware address of lance */
+
+/* Index to functions, as function prototypes.
+ */
+static void	start (int target);
+static int	stop (int target);
+static int	testpkt (int target);
+static int	sendpkt (int target, unsigned char *buffer, int length);
+static int	receivepkt (int target, unsigned char *buffer);
+static int	inquiry (int target, unsigned char *buffer);
+static HADDR	*read_hw_addr(int target, unsigned char *buffer);
+static void	setup_dma (void *address, unsigned rw_flag, int num_blocks);
+static int	send_first (int target, unsigned char byte);
+static int	send_1_5 (int lun, unsigned char *command, int dma);
+static int	get_status (void);
+static int	calc_received (void *start_address);
+
+extern int pamsnet_probe(struct device *dev);
+
+static int pamsnet_open(struct device *dev);
+static int pamsnet_send_packet(struct sk_buff *skb, struct device *dev);
+static void pamsnet_poll_rx(struct device *);
+static int pamsnet_close(struct device *dev);
+static struct enet_statistics *net_get_stats(struct device *dev);
+static void pamsnet_tick(unsigned long);
+
+static void pamsnet_intr(int irq, void *data, struct pt_regs *fp);
+
+static struct timer_list pamsnet_timer = { NULL, NULL, 0, 0, pamsnet_tick };
+
+#define STRAM_ADDR(a)	(((a) & 0xff000000) == 0)
+
+typedef struct
+{
+	unsigned char reserved1[0x38];
+	HADDR  hwaddr;
+	unsigned char reserved2[0x1c2];
+} DMAHWADDR;
+
+/*
+ * Definitions of commands understood by the PAMs DMA adaptor.
+ *
+ * In general the DMA adaptor uses LUN 0, 5, 6 and 7 on one ID changeable
+ * by the PAM's Net software.
+ *
+ * LUN 0 works as a harddisk. You can boot the PAM's Net driver there.
+ * LUN 5 works as a harddisk and lets you access the RAM and some I/O HW
+ *       area. In sector 0, bytes 0x38-0x3d you find the ethernet HW address
+ *       of the adaptor.
+ * LUN 6 works as a harddisk and lets you access the firmware ROM.
+ * LUN 7 lets you send and receive packets.
+ *
+ * Some commands like the INQUIRY command work identical on all used LUNs.
+ * 
+ * UNKNOWN1 seems to read some data.
+ *          Command length is 6 bytes.
+ * UNKNOWN2 seems to read some data (command byte 1 must be !=0). The
+ *          following bytes seem to be something like an allocation length.
+ *          Command length is 6 bytes.
+ * READPKT  reads a packet received by the DMA adaptor.
+ *          Command length is 6 bytes.
+ * WRITEPKT sends a packet transferred by the following DMA phase. The length
+ *          of the packet is transferred in command bytes 3 and 4.
+ *          The adaptor automatically replaces the src hw address in an ethernet
+ *          packet by its own hw address.
+ *          Command length is 6 bytes.
+ * INQUIRY  has the same function as the INQUIRY command supported by harddisks
+ *          and other SCSI devices. It lets you detect which device you found
+ *          at a given address.
+ *          Command length is 6 bytes.
+ * START    initializes the DMA adaptor. After this command it is able to send
+ *          and receive packets. There is no status byte returned!
+ *          Command length is 1 byte.
+ * NUMPKTS  gives back the number of received packets waiting in the queue in
+ *          the status byte.
+ *          Command length is 1 byte.
+ * UNKNOWN3
+ * UNKNOWN4 Function of these three commands is unknown.
+ * UNKNOWN5 The command length of these three commands is 1 byte.
+ * DESELECT immediately deselects the DMA adaptor. May important with interrupt
+ *          driven operation.
+ *          Command length is 1 byte.
+ * STOP     resets the DMA adaptor. After this command packets can no longer
+ *          be received or transferred.
+ *          Command length is 6 byte.
+ */
+
+enum {UNKNOWN1=3, READPKT=8, UNKNOWN2, WRITEPKT=10, INQUIRY=18, START, 
+      NUMPKTS=22, UNKNOWN3, UNKNOWN4, UNKNOWN5, DESELECT, STOP};
+
+#define READSECTOR  READPKT
+#define WRITESECTOR WRITEPKT
+
+u_char *inquire8="MV      PAM's NET/GK";
+
+#define DMALOW   dma_wd.dma_lo
+#define DMAMID   dma_wd.dma_md
+#define DMAHIGH  dma_wd.dma_hi
+#define DACCESS  dma_wd.fdc_acces_seccount
+
+#define MFP_GPIP mfp.par_dt_reg
+
+/* Some useful functions */
+
+#define INT      (!(MFP_GPIP & 0x20))
+#define DELAY ({MFP_GPIP; MFP_GPIP; MFP_GPIP;})
+#define WRITEMODE(value)					\
+	({	u_short dummy = value;				\
+		__asm__ volatile("movew %0, 0xFFFF8606" : : "d"(dummy));	\
+		DELAY;						\
+	})
+#define WRITEBOTH(value1, value2)				\
+	({	u_long dummy = (u_long)(value1)<<16 | (u_short)(value2);	\
+		__asm__ volatile("movel %0, 0xFFFF8604" : : "d"(dummy));	\
+		DELAY;						\
+	})
+
+/* Definitions for DMODE */
+
+#define READ        0x000
+#define WRITE       0x100
+
+#define DMA_FDC     0x080
+#define DMA_ACSI    0x000
+
+#define DMA_DISABLE 0x040
+
+#define SEC_COUNT   0x010
+#define DMA_WINDOW  0x000
+
+#define REG_ACSI    0x008
+#define REG_FDC     0x000
+
+#define A1          0x002
+
+/* Timeout constants */
+
+#define TIMEOUTCMD HZ/2   /* ca. 500ms */
+#define TIMEOUTDMA HZ     /* ca. 1s */
+#define COMMAND_DELAY 500 /* ca. 0.5ms */
+
+unsigned rw;
+int lance_target = -1;
+int if_up = 0;
+
+/* The following routines access the ethernet board connected to the
+ * ACSI port via the st_dma chip.
+ */
+
+/* The following lowlevel routines work on physical addresses only and assume
+ * that eventually needed buffers are 
+ * - completely located in ST RAM
+ * - are contigous in the physical address space
+ */
+
+/* Setup the DMA counter */
+
+static void 
+setup_dma (address, rw_flag, num_blocks)
+	void *address;
+	unsigned rw_flag;
+	int num_blocks;
+{
+	WRITEMODE((unsigned) rw_flag          | DMA_FDC | SEC_COUNT | REG_ACSI |
+		  A1);
+	WRITEMODE((unsigned)(rw_flag ^ WRITE) | DMA_FDC | SEC_COUNT | REG_ACSI |
+		  A1);
+	WRITEMODE((unsigned) rw_flag          | DMA_FDC | SEC_COUNT | REG_ACSI |
+		  A1);
+	DMALOW  = (unsigned char)((unsigned long)address & 0xFF);
+	DMAMID  = (unsigned char)(((unsigned long)address >>  8) & 0xFF);
+	DMAHIGH = (unsigned char)(((unsigned long)address >> 16) & 0xFF);
+	WRITEBOTH((unsigned)num_blocks & 0xFF,
+		  rw_flag | DMA_FDC | DMA_WINDOW | REG_ACSI | A1);
+	rw = rw_flag;
+}
+
+/* Send the first byte of an command block */
+
+static int 
+send_first (target, byte)
+	int target;
+	unsigned char byte;
+{
+	rw = READ;
+	acsi_delay_end(COMMAND_DELAY);
+	/*
+	 * wake up ACSI
+	 */
+	WRITEMODE(DMA_FDC | DMA_WINDOW | REG_ACSI);
+	/*
+	 * write command byte
+	 */
+	WRITEBOTH((target << 5) | (byte & 0x1F), DMA_FDC |
+	          DMA_WINDOW | REG_ACSI | A1);
+	return (!acsi_wait_for_IRQ(TIMEOUTCMD));
+}
+
+/* Send the rest of an command block */
+
+static int 
+send_1_5 (lun, command, dma)
+	int lun;
+	unsigned char *command;
+	int dma;
+{
+	int i, j;
+	
+	for (i=0; i<5; i++) {
+		WRITEBOTH((!i ? (((lun & 0x7) << 5) | (command[i] & 0x1F))
+			      : command[i]),
+			  rw | REG_ACSI | DMA_WINDOW |
+			   ((i < 4) ? DMA_FDC
+				    : (dma ? DMA_ACSI
+					   : DMA_FDC)) | A1);
+		if (i < 4 && (j = !acsi_wait_for_IRQ(TIMEOUTCMD)))
+			return (j);
+	}
+	return (0);
+}
+
+/* Read a status byte */
+
+static int 
+get_status (void)
+{
+	WRITEMODE(DMA_FDC | DMA_WINDOW | REG_ACSI | A1);
+	acsi_delay_start();
+	return ((int)(DACCESS & 0xFF));
+}
+
+/* Calculate the number of received bytes */
+
+static int 
+calc_received (start_address)
+	void *start_address;
+{
+	return (int)(
+		(((unsigned long)DMAHIGH << 16) | ((unsigned)DMAMID << 8) | DMALOW)
+	      - (unsigned long)start_address);
+}
+
+/* The following midlevel routines still work on physical addresses ... */
+
+/* start() starts the PAM's DMA adaptor */
+
+static void
+start (target)
+	int target;
+{
+	send_first(target, START);
+}
+
+/* stop() stops the PAM's DMA adaptor and returns a value of zero in case of success */
+
+static int
+stop (target)
+	int target;
+{
+	int ret = -1;
+	unsigned char cmd_buffer[5];
+
+	if (send_first(target, STOP))
+		goto bad;
+	cmd_buffer[0] = cmd_buffer[1] = cmd_buffer[2] =
+	cmd_buffer[3] = cmd_buffer[4] = 0;
+	if (send_1_5(7, cmd_buffer, 0) ||
+	    !acsi_wait_for_IRQ(TIMEOUTDMA) ||
+	    get_status())
+		goto bad;
+	ret = 0;
+bad:
+	return (ret);
+}
+
+/* testpkt() returns the number of received packets waiting in the queue */
+
+static int
+testpkt(target)
+	int target;
+{
+	int ret = -1;
+
+	if (send_first(target, NUMPKTS))
+		goto bad;
+	ret = get_status();
+bad:
+	return (ret);
+}
+
+/* inquiry() returns 0 when PAM's DMA found, -1 when timeout, -2 otherwise */
+/* Please note: The buffer is for internal use only but must be defined!   */
+
+static int 
+inquiry (target, buffer)
+	int target;
+	unsigned char *buffer;
+{
+	int ret = -1;
+	unsigned char *vbuffer = (unsigned char *)PTOV(buffer);
+	unsigned char cmd_buffer[5];
+	
+	if (send_first(target, INQUIRY))
+		goto bad;
+	setup_dma(buffer, READ, 1);
+	vbuffer[8] = vbuffer[27] = 0; /* Avoid confusion with previous read data */
+	cmd_buffer[0] = cmd_buffer[1] = cmd_buffer[2] = cmd_buffer[4] = 0;
+	cmd_buffer[3] = 48;
+	if (send_1_5(5, cmd_buffer, 1) ||
+	    !acsi_wait_for_IRQ(TIMEOUTDMA) ||
+	    get_status() ||
+	    (calc_received(buffer) < 32))
+		goto bad;
+	dma_cache_maintenance((unsigned long)(buffer+8), 20, 0);
+	if (memcmp(inquire8, vbuffer+8, 20))
+		goto bad;
+	ret = 0;
+bad:
+	if (!!NET_DEBUG) {
+		vbuffer[8+20]=0;
+		printk("inquiry of target %d: %s\n", target, vbuffer+8);
+	}
+	return (ret);
+}
+
+/* 
+ * read_hw_addr() reads the sector containing the hwaddr and returns
+ * a pointer to it (virtual address!) or 0 in case of an error
+ */
+
+static HADDR 
+*read_hw_addr(target, buffer)
+	int target;
+	unsigned char *buffer;
+{
+	HADDR *ret = 0;
+	unsigned char cmd_buffer[5];
+
+	if (send_first(target, READSECTOR))
+		goto bad;
+	setup_dma(buffer, READ, 1);
+	cmd_buffer[0] = cmd_buffer[1] = cmd_buffer[2] = cmd_buffer[4] = 0;
+	cmd_buffer[3] = 1;
+	if (send_1_5(5, cmd_buffer, 1) ||
+	    !acsi_wait_for_IRQ(TIMEOUTDMA) ||
+	    get_status())
+		goto bad;
+	ret = (HADDR *)PTOV(&(((DMAHWADDR *)buffer)->hwaddr));
+	dma_cache_maintenance((unsigned long)buffer, 512, 0);
+bad:
+	return (ret);
+}
+
+static void
+pamsnet_intr(irq, data, fp)
+	int irq;
+	void *data;
+	struct pt_regs *fp;
+{
+	return;
+}
+
+/* receivepkt() loads a packet to a given buffer and returns its length */
+
+static int
+receivepkt (target, buffer)
+	int target;
+	unsigned char *buffer;
+{
+	int ret = -1;
+	unsigned char cmd_buffer[5];
+
+	if (send_first(target, READPKT))
+		goto bad;
+	setup_dma(buffer, READ, 3);
+	cmd_buffer[0] = cmd_buffer[1] = cmd_buffer[2] = cmd_buffer[4] = 0;
+	cmd_buffer[3] = 3;
+	if (send_1_5(7, cmd_buffer, 1) ||
+	    !acsi_wait_for_IRQ(TIMEOUTDMA) ||
+	    get_status())
+		goto bad;
+	ret = calc_received(buffer);
+bad:
+	return (ret);
+}
+
+/* sendpkt() sends a packet and returns a value of zero when the packet was sent
+             successfully */
+
+static int
+sendpkt (target, buffer, length)
+	int target;
+	unsigned char *buffer;
+	int length;
+{
+	int ret = -1;
+	unsigned char cmd_buffer[5];
+
+	if (send_first(target, WRITEPKT))
+		goto bad;
+	setup_dma(buffer, WRITE, 3);
+	cmd_buffer[0] = cmd_buffer[1] = cmd_buffer[4] = 0;
+	cmd_buffer[2] = length >> 8;
+	cmd_buffer[3] = length & 0xFF;
+	if (send_1_5(7, cmd_buffer, 1) ||
+	    !acsi_wait_for_IRQ(TIMEOUTDMA) ||
+	    get_status())
+		goto bad;
+	ret = 0;
+bad:
+	return (ret);
+}
+
+/* The following higher level routines work on virtual addresses and convert them to
+ * physical addresses when passed to the lowlevel routines. It's up to the higher level
+ * routines to copy data from Alternate RAM to ST RAM if neccesary!
+ */
+
+/* Check for a network adaptor of this type, and return '0' if one exists.
+ */
+
+extern int
+pamsnet_probe (dev)
+	struct device *dev;
+{
+	int i;
+	HADDR *hwaddr;
+	
+	unsigned char station_addr[6];
+	static unsigned version_printed = 0;
+	/* avoid "Probing for..." printed 4 times - the driver is supporting only one adapter now! */
+	static int no_more_found = 0;
+
+	if (no_more_found)
+		return ENODEV;
+
+	no_more_found = 1;
+	
+	printk("Probing for PAM's Net/GK Adapter...\n");
+
+	/* Allocate the DMA buffer here since we need it for probing! */
+
+	nic_packet = (struct nic_pkt_s *)acsi_buffer;
+	phys_nic_packet = (unsigned char *)phys_acsi_buffer;
+	if (pamsnet_debug > 0) {
+		printk("nic_packet at 0x%p, phys at 0x%p\n",
+			   nic_packet, phys_nic_packet );
+	}
+
+	stdma_lock(pamsnet_intr, NULL);
+	DISABLE_IRQ();
+
+	for (i=0; i<8; i++) {
+		/* Do two inquiries to cover cases with strange equipment on previous ID */
+		/* blocking the ACSI bus (like the SLMC804 laser printer controller...   */
+		inquiry(i, phys_nic_packet);
+		if (!inquiry(i, phys_nic_packet)) {
+			lance_target = i;
+			break;
+		}
+	}
+
+	if (!!NET_DEBUG)
+		printk("ID: %d\n",i);
+
+	if (lance_target >= 0) {
+		if (!(hwaddr = read_hw_addr(lance_target, phys_nic_packet)))
+			lance_target = -1;
+		else
+			memcpy (station_addr, hwaddr, ETH_ALEN);
+	}
+	
+	ENABLE_IRQ();
+	stdma_release();
+
+	if (lance_target < 0)
+		printk("No PAM's Net/GK found.\n");
+
+	if ((dev == NULL) || (lance_target < 0))
+		return ENODEV;
+	if (pamsnet_debug > 0 && version_printed++ == 0)
+		printk(version);
+
+	printk("%s: %s found on target %01d, eth-addr: %02x:%02x:%02x:%02x:%02x:%02x.\n",
+		dev->name, "PAM's Net/GK", lance_target,
+		station_addr[0], station_addr[1], station_addr[2],
+		station_addr[3], station_addr[4], station_addr[5]);
+
+	/* Initialize the device structure. */
+	if (dev->priv == NULL)
+		dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+	memset(dev->priv, 0, sizeof(struct net_local));
+
+	dev->open		= pamsnet_open;
+	dev->stop		= pamsnet_close;
+	dev->hard_start_xmit	= pamsnet_send_packet;
+	dev->get_stats		= net_get_stats;
+
+	/* Fill in the fields of the device structure with ethernet-generic
+	 * values. This should be in a common file instead of per-driver.
+	 */
+
+	for (i = 0; i < ETH_ALEN; i++) {
+#if 0
+		dev->broadcast[i] = 0xff;
+#endif
+		dev->dev_addr[i]  = station_addr[i];
+	}
+	ether_setup(dev);
+
+	return(0);
+}
+
+/* Open/initialize the board.  This is called (in the current kernel)
+   sometime after booting when the 'ifconfig' program is run.
+
+   This routine should set everything up anew at each open, even
+   registers that "should" only need to be set once at boot, so that
+   there is non-reboot way to recover if something goes wrong.
+ */
+static int
+pamsnet_open(struct device *dev) {
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	if (pamsnet_debug > 0)
+		printk("pamsnet_open\n");
+	stdma_lock(pamsnet_intr, NULL);
+	DISABLE_IRQ();
+
+	/* Reset the hardware here.
+	 */
+	if (!if_up)
+		start(lance_target);
+	if_up = 1;
+	lp->open_time = 0;	/*jiffies*/
+	lp->poll_time = MAX_POLL_TIME;
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	ENABLE_IRQ();
+	stdma_release();
+	pamsnet_timer.data = (long)dev;
+	pamsnet_timer.expires = jiffies + lp->poll_time;
+	add_timer(&pamsnet_timer);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int
+pamsnet_send_packet(struct sk_buff *skb, struct device *dev) {
+	struct net_local *lp = (struct net_local *)dev->priv;
+	unsigned long flags;
+
+	/* If some higher layer thinks we've missed an tx-done interrupt we
+	 * are passed NULL. Caution: dev_tint() handles the cli()/sti() itself.
+	 */
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+	/* Block a timer-based transmit from overlapping.  This could better be
+	 * done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+	 */
+	save_flags(flags);
+	cli();
+
+	if (stdma_islocked()) {
+		restore_flags(flags);
+		lp->stats.tx_errors++;
+	}
+	else {
+		int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+		unsigned long buf = VTOP(skb->data);
+		int stat;
+
+		stdma_lock(pamsnet_intr, NULL);
+		DISABLE_IRQ();
+
+		restore_flags(flags);
+		if( !STRAM_ADDR(buf+length-1) ) {
+			memcpy(nic_packet->buffer, skb->data, length);
+			buf = (unsigned long)phys_nic_packet;
+		}
+
+		dma_cache_maintenance(buf, length, 1);
+
+		stat = sendpkt(lance_target, (unsigned char *)buf, length);
+		ENABLE_IRQ();
+		stdma_release();
+
+		dev->trans_start = jiffies;
+		dev->tbusy	 = 0;
+	}
+	dev_kfree_skb(skb, FREE_WRITE);
+
+	/* You might need to clean up and record Tx statistics here.
+	 */
+	lp->stats.tx_packets++;
+	return 0;
+}
+
+/* We have a good packet(s), get it/them out of the buffers.
+ */
+static void
+pamsnet_poll_rx(struct device *dev) {
+	struct net_local *lp = (struct net_local *)dev->priv;
+	int boguscount;
+	int pkt_len;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	/* ++roman: Take care at locking the ST-DMA... This must be done with ints
+	 * off, since otherwise an int could slip in between the question and the
+	 * locking itself, and then we'd go to sleep... And locking itself is
+	 * necessary to keep the floppy_change timer from working with ST-DMA
+	 * registers. */
+	if (stdma_islocked()) {
+		restore_flags(flags);
+		return;
+	}
+	stdma_lock(pamsnet_intr, NULL);
+	DISABLE_IRQ();
+	restore_flags(flags);
+
+	boguscount = testpkt(lance_target);
+	if( lp->poll_time < MAX_POLL_TIME ) lp->poll_time++;
+
+	while(boguscount--) {
+		pkt_len = receivepkt(lance_target, phys_nic_packet);
+	  
+		if( pkt_len < 60 ) break;
+
+		/* Good packet... */
+
+		dma_cache_maintenance((unsigned long)phys_nic_packet, pkt_len, 0);
+
+		lp->poll_time = pamsnet_min_poll_time;    /* fast poll */
+		if( pkt_len >= 60 && pkt_len <= 2048 ) {
+			if (pkt_len > 1514)
+				pkt_len = 1514;
+
+			/* Malloc up new buffer.
+			 */
+			skb = alloc_skb(pkt_len, GFP_ATOMIC);
+			if (skb == NULL) {
+				printk("%s: Memory squeeze, dropping packet.\n",
+					dev->name);
+				lp->stats.rx_dropped++;
+				break;
+			}
+			skb->len = pkt_len;
+			skb->dev = dev;
+
+			/* 'skb->data' points to the start of sk_buff data area.
+			 */
+			memcpy(skb->data, nic_packet->buffer, pkt_len);
+			netif_rx(skb);
+			lp->stats.rx_packets++;
+		}
+	}
+
+	/* If any worth-while packets have been received, dev_rint()
+	   has done a mark_bh(INET_BH) for us and will work on them
+	   when we get to the bottom-half routine.
+	 */
+
+	ENABLE_IRQ();
+	stdma_release();
+	return;
+}
+
+/* pamsnet_tick: called by pamsnet_timer. Reads packets from the adapter,
+ * passes them to the higher layers and restarts the timer.
+ */
+static void
+pamsnet_tick(unsigned long data) {
+	struct device	 *dev = (struct device *)data;
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	if( pamsnet_debug > 0 && (lp->open_time++ & 7) == 8 )
+		printk("pamsnet_tick: %ld\n", lp->open_time);
+
+	pamsnet_poll_rx(dev);
+
+	pamsnet_timer.expires = jiffies + lp->poll_time;
+	add_timer(&pamsnet_timer);
+}
+
+/* The inverse routine to pamsnet_open().
+ */
+static int
+pamsnet_close(struct device *dev) {
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	if (pamsnet_debug > 0)
+		printk("pamsnet_close, open_time=%ld\n", lp->open_time);
+	del_timer(&pamsnet_timer);
+	stdma_lock(pamsnet_intr, NULL);
+	DISABLE_IRQ();
+
+	if (if_up)
+		stop(lance_target);
+	if_up = 0;
+
+	lp->open_time = 0;
+
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	ENABLE_IRQ();
+	stdma_release();
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* Get the current statistics.
+   This may be called with the card open or closed.
+ */
+static struct enet_statistics *
+net_get_stats(struct device *dev) {
+	struct net_local *lp = (struct net_local *)dev->priv;
+	return &lp->stats;
+}
+
+
+#ifdef MODULE
+
+#include <linux/version.h>
+
+/* We should include the kernel identification string in the module.
+ */
+static char kernel_version[] = UTS_RELEASE;
+
+#undef	NEXT_DEV
+#define NEXT_DEV	(&pam_dev)
+
+static struct device pam_dev =  
+	{
+		"        ",	/* filled in by register_netdev() */
+		0, 0, 0, 0,	/* memory */
+		0, 0,		/* base, irq */
+		0, 0, 0, NEXT_DEV, pamsnet_probe,
+	};
+
+int
+init_module(void) {
+	int err;
+	
+	if ((err = register_netdev(&pam_dev))) {
+		if (err == -EEXIST)  {
+			printk("PAM's Net/GK: devices already present. Module not loaded.\n");
+		}
+		return err;
+	}
+	return 0;
+}  
+
+void
+cleanup_module(void) {
+	unregister_netdev(&pam_dev);
+}
+                      
+#endif /* MODULE */
+
+/* Local variables:
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include
+	-b m68k-linuxaout -Wall -Wstrict-prototypes -O2
+	-fomit-frame-pointer -pipe -DMODULE -I../../net/inet -c atari_pamsnet.c"
+ *  version-control: t
+ *  kept-new-versions: 5
+ *  tab-width: 8
+ * End:
+ */
diff -urN pc-2.0.21/drivers/net/atarilance.c linux-2.0.21/drivers/net/atarilance.c
--- pc-2.0.21/drivers/net/atarilance.c	Fri Apr 26 11:12:25 1996
+++ linux-2.0.21/drivers/net/atarilance.c	Sat Aug 24 14:36:28 1996
@@ -56,12 +56,12 @@
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 
+#include <asm/setup.h>
 #include <asm/irq.h>
 #include <asm/atarihw.h>
 #include <asm/atariints.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
-#include <asm/bootinfo.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -340,8 +340,7 @@
 static int lance_open( struct device *dev );
 static void lance_init_ring( struct device *dev );
 static int lance_start_xmit( struct sk_buff *skb, struct device *dev );
-static void lance_interrupt( int irq, struct pt_regs *fp, struct device
-                             *dev );
+static void lance_interrupt( int irq, void *dev_id, struct pt_regs *fp );
 static int lance_rx( struct device *dev );
 static int lance_close( struct device *dev );
 static struct enet_statistics *lance_get_stats( struct device *dev );
@@ -533,8 +532,8 @@
 	if (lp->cardtype == PAM_CARD ||
 		memaddr == (unsigned short *)0xffe00000) {
 		/* PAMs card and Riebl on ST use level 5 autovector */
-		add_isr( IRQ_AUTO_5, (isrfunc)lance_interrupt, IRQ_TYPE_PRIO, dev,
-				 "PAM/Riebl-ST Ethernet" );
+		request_irq(IRQ_AUTO_5, lance_interrupt, IRQ_TYPE_PRIO,
+		            "PAM/Riebl-ST Ethernet", dev);
 		dev->irq = (unsigned short)IRQ_AUTO_5;
 	}
 	else {
@@ -547,8 +546,8 @@
 			printk( "Lance: request for VME interrupt failed\n" );
 			return( 0 );
 		}
-		add_isr( irq, (isrfunc)lance_interrupt, IRQ_TYPE_PRIO, dev,
-				 "Riebl-VME Ethernet" );
+		request_irq(irq, lance_interrupt, IRQ_TYPE_PRIO,
+		            "Riebl-VME Ethernet", dev);
 		dev->irq = irq;
 	}
 
@@ -846,9 +845,10 @@
 
 /* The LANCE interrupt handler. */
 
-static void lance_interrupt( int irq, struct pt_regs *fp, struct device *dev )
-
-{	struct lance_private *lp;
+static void lance_interrupt( int irq, void *dev_id, struct pt_regs *fp)
+{
+	struct device *dev = dev_id;
+	struct lance_private *lp;
 	struct lance_ioreg	 *IO;
 	int csr0, boguscnt = 10;
 
diff -urN pc-2.0.21/drivers/net/hydra.c linux-2.0.21/drivers/net/hydra.c
--- pc-2.0.21/drivers/net/hydra.c	Mon May 20 06:54:28 1996
+++ linux-2.0.21/drivers/net/hydra.c	Sat Aug 24 14:37:00 1996
@@ -28,11 +28,11 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
+#include <asm/setup.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 
-#include <asm/bootinfo.h>
 #include <asm/amigaints.h>
 #include <asm/amigahw.h>
 #include <asm/zorro.h>
@@ -92,7 +92,7 @@
 
 static int hydra_open(struct device *dev);
 static int hydra_start_xmit(struct sk_buff *skb, struct device *dev);
-static void hydra_interrupt(int irq, struct pt_regs *fp, void *data);
+static void hydra_interrupt(int irq, void *data, struct pt_regs *fp);
 static void __inline__ hydra_rx(struct device *dev, struct hydra_private *priv, volatile u_char *nicbase);
 static int hydra_close(struct device *dev);
 static struct enet_statistics *hydra_get_stats(struct device *dev);
@@ -272,7 +272,7 @@
     dev->interrupt = 0;
     dev->start = 1;
     
-    if(!add_isr(IRQ_AMIGA_PORTS, hydra_interrupt, 0, dev, "Hydra Ethernet"))
+    if(request_irq(IRQ_AMIGA_PORTS, hydra_interrupt, 0, "Hydra Ethernet", dev))
       return(-EAGAIN);
 
     MOD_INC_USE_COUNT;
@@ -300,7 +300,7 @@
   /* wait for NIC to stop (what a nice timeout..) */
   while(((READ_REG(NIC_ISR) & ISR_RST) == 0) && --n);
     
-  remove_isr(IRQ_AMIGA_PORTS, hydra_interrupt, dev);
+  free_irq(IRQ_AMIGA_PORTS, dev);
 
   MOD_DEC_USE_COUNT;
 
@@ -308,7 +308,7 @@
 }
 
 
-static void hydra_interrupt(int irq, struct pt_regs *fp, void *data)
+static void hydra_interrupt(int irq, void *data, struct pt_regs *fp)
     {
     volatile u_char *nicbase;
   
diff -urN pc-2.0.21/drivers/net/slip.h linux-2.0.21/drivers/net/slip.h
--- pc-2.0.21/drivers/net/slip.h	Fri Apr 12 08:49:39 1996
+++ linux-2.0.21/drivers/net/slip.h	Thu Sep 26 23:06:59 1996
@@ -87,7 +87,7 @@
   int			xdata, xbits;	/* 6 bit slip controls 		*/
 #endif
 
-  unsigned char		flags;		/* Flag values/ mode etc	*/
+  unsigned long		flags;		/* Flag values/ mode etc	*/
 #define SLF_INUSE	0		/* Channel in use               */
 #define SLF_ESCAPE	1               /* ESC received                 */
 #define SLF_ERROR	2               /* Parity, etc. error           */
diff -urN pc-2.0.21/drivers/scsi/Makefile linux-2.0.21/drivers/scsi/Makefile
--- pc-2.0.21/drivers/scsi/Makefile	Sat Jul  6 11:06:37 1996
+++ linux-2.0.21/drivers/scsi/Makefile	Mon Aug 26 23:57:49 1996
@@ -8,8 +8,13 @@
 
 ifdef CONFIG_KERNEL_ELF
 # This is used for ELF - it needs to migrate or be moved.
+ifeq ($(ARCH),i386)
 LD_RFLAG = -m elf_i386
 endif
+ifeq ($(ARCH),m68k)
+LD_RFLAG = -m m68kelf
+endif
+endif
 
 L_TARGET := scsi.a
 L_OBJS   := 
@@ -26,7 +31,7 @@
 ifeq (${CFLAGS},)
 CFLAGS = -D__KERNEL__=1 \
 	-DMODULE -Wall -Wstrict-prototypes -I. -I../../include \
-	-O2 -fomit-frame-pointer -m486
+	-O2 -fomit-frame-pointer
 
 include ../../.config
 
@@ -108,6 +113,14 @@
 else
   ifeq ($(CONFIG_GVP11_SCSI),m)
   M_OBJS += gvp11.o wd33c93.o
+  endif
+endif
+
+ifeq ($(CONFIG_CYBERSTORM_SCSI),y)
+L_OBJS += esp.o cyber_esp.o
+else
+  ifeq ($(CONFIG_CYBERSTORM_SCSI),m)
+  M_OBJS += esp.o cyber_esp.o
   endif
 endif
 
diff -urN pc-2.0.21/drivers/scsi/a2091.c linux-2.0.21/drivers/scsi/a2091.c
--- pc-2.0.21/drivers/scsi/a2091.c	Fri Apr 26 09:30:51 1996
+++ linux-2.0.21/drivers/scsi/a2091.c	Thu Sep 26 19:20:38 1996
@@ -1,11 +1,12 @@
 #include <linux/types.h>
 #include <linux/mm.h>
 #include <linux/blk.h>
+#include <linux/sched.h>
 #include <linux/version.h>
 
+#include <asm/setup.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
-#include <asm/bootinfo.h>
 #include <asm/amigaints.h>
 #include <asm/amigahw.h>
 #include <asm/zorro.h>
@@ -29,7 +30,7 @@
 static struct Scsi_Host *first_instance = NULL;
 static Scsi_Host_Template *a2091_template;
 
-static void a2091_intr (int irq, struct pt_regs *fp, void *dummy)
+static void a2091_intr (int irq, void *dummy, struct pt_regs *fp)
 {
     unsigned int status;
     struct Scsi_Host *instance;
@@ -191,39 +192,35 @@
 {
     static unsigned char called = 0;
     struct Scsi_Host *instance;
-    int i, manuf, product, num_a2091 = 0;
+    int num_a2091 = 0;
     caddr_t address;
+    int key;
+    struct ConfigDev *cd;
 
     if (!MACH_IS_AMIGA || called)
 	return 0;
     called = 1;
 
     tpnt->proc_dir = &proc_scsi_a2091;
+    tpnt->proc_info = &wd33c93_proc_info;
 
-    for (i = 0; i < boot_info.bi_amiga.num_autocon; i++)
-    {
-	manuf = boot_info.bi_amiga.autocon[i].cd_Rom.er_Manufacturer;
-	product = boot_info.bi_amiga.autocon[i].cd_Rom.er_Product;
-	if (manuf == MANUF_COMMODORE && (product == PROD_A2091 ||
-					 product == PROD_A590)) {
-	    address = boot_info.bi_amiga.autocon[i].cd_BoardAddr;
-	    instance = scsi_register (tpnt,
-				      sizeof (struct WD33C93_hostdata));
-	    instance->base = (unsigned char *)ZTWO_VADDR(address);
-	    DMA(instance)->DAWR = DAWR_A2091;
-	    wd33c93_init(instance, (wd33c93_regs *)&(DMA(instance)->SASR),
-			 dma_setup, dma_stop, WD33C93_FS_8_10);
-	    if (num_a2091++ == 0) {
-		first_instance = instance;
-		a2091_template = instance->hostt;
-		add_isr(IRQ_AMIGA_PORTS, a2091_intr, 0, NULL, "A2091 SCSI");
-	    }
-	    DMA(instance)->CNTR = CNTR_PDMD | CNTR_INTEN;
-
-#if 0 /* The Zorro stuff is not totally integrated yet ! */
-	    boot_info.bi_amiga.autocon_configured |= 1<<i;
-#endif
-	  }
+    while ((key = zorro_find(MANUF_COMMODORE, PROD_A2091, 0, 0)) ||
+	   (key = zorro_find(MANUF_COMMODORE, PROD_A590, 0, 0))) {
+	cd = zorro_get_board(key);
+	address = cd->cd_BoardAddr;
+	instance = scsi_register (tpnt, sizeof (struct WD33C93_hostdata));
+	instance->base = (unsigned char *)ZTWO_VADDR(address);
+	instance->irq = IRQ_AMIGA_PORTS & ~IRQ_MACHSPEC;
+	DMA(instance)->DAWR = DAWR_A2091;
+	wd33c93_init(instance, (wd33c93_regs *)&(DMA(instance)->SASR),
+		     dma_setup, dma_stop, WD33C93_FS_8_10);
+	if (num_a2091++ == 0) {
+	    first_instance = instance;
+	    a2091_template = instance->hostt;
+	    request_irq(IRQ_AMIGA_PORTS, a2091_intr, 0, "A2091 SCSI", a2091_intr);
+	}
+	DMA(instance)->CNTR = CNTR_PDMD | CNTR_INTEN;
+	zorro_config_board(key, 0);
     }
 
     return num_a2091;
diff -urN pc-2.0.21/drivers/scsi/a3000.c linux-2.0.21/drivers/scsi/a3000.c
--- pc-2.0.21/drivers/scsi/a3000.c	Fri Apr 26 09:30:51 1996
+++ linux-2.0.21/drivers/scsi/a3000.c	Tue Sep 24 20:02:16 1996
@@ -1,11 +1,12 @@
 #include <linux/types.h>
 #include <linux/mm.h>
 #include <linux/blk.h>
+#include <linux/sched.h>
 #include <linux/version.h>
 
+#include <asm/setup.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
-#include <asm/bootinfo.h>
 #include <asm/amigaints.h>
 #include <asm/amigahw.h>
 #include <asm/irq.h>
@@ -27,7 +28,7 @@
 
 static struct Scsi_Host *a3000_host = NULL;
 
-static void a3000_intr (int irq, struct pt_regs *fp, void *dummy)
+static void a3000_intr (int irq, void *dummy, struct pt_regs *fp)
 {
     unsigned int status = DMA(a3000_host)->ISTR;
 
@@ -96,26 +97,12 @@
     /* setup DMA *physical* address */
     DMA(a3000_host)->ACR = addr;
 
-
     if (dir_in)
-      {
   	/* invalidate any cache */
-        /*
-         * On the 68040 it's not ok to use cache_clear, as it just invalidates
-         * cache-lines, and thereby trashing them. We need to use cache_push
-         * to avoid problems/crashes.
-         * This was a real bitch to catch :-( -Jes
-         */
-
-        if (boot_info.cputype & CPU_68040)
-	  cache_push (addr, cmd->SCp.this_residual);
-	else
-	  cache_clear (addr, cmd->SCp.this_residual);
-      }
+	cache_clear (addr, cmd->SCp.this_residual);
     else
-      /* push any dirty cache */
-      cache_push (addr, cmd->SCp.this_residual);
-      
+	/* push any dirty cache */
+	cache_push (addr, cmd->SCp.this_residual);
 
     /* start DMA */
     DMA(a3000_host)->ST_DMA = 1;
@@ -190,13 +177,15 @@
 	return 0;
 
     tpnt->proc_dir = &proc_scsi_a3000;
+    tpnt->proc_info = &wd33c93_proc_info;
 
     a3000_host = scsi_register (tpnt, sizeof(struct WD33C93_hostdata));
     a3000_host->base = (unsigned char *)ZTWO_VADDR(0xDD0000);
+    a3000_host->irq = IRQ_AMIGA_PORTS & ~IRQ_MACHSPEC;
     DMA(a3000_host)->DAWR = DAWR_A3000;
     wd33c93_init(a3000_host, (wd33c93_regs *)&(DMA(a3000_host)->SASR),
 		 dma_setup, dma_stop, WD33C93_FS_12_15);
-    add_isr(IRQ_AMIGA_PORTS, a3000_intr, 0, NULL, "A3000 SCSI");
+    request_irq(IRQ_AMIGA_PORTS, a3000_intr, 0, "A3000 SCSI", a3000_intr);
     DMA(a3000_host)->CNTR = CNTR_PDMD | CNTR_INTEN;
     called = 1;
 
diff -urN pc-2.0.21/drivers/scsi/atari_NCR5380.c linux-2.0.21/drivers/scsi/atari_NCR5380.c
--- pc-2.0.21/drivers/scsi/atari_NCR5380.c	Sat May 18 10:15:09 1996
+++ linux-2.0.21/drivers/scsi/atari_NCR5380.c	Mon Sep 16 23:53:57 1996
@@ -47,8 +47,8 @@
  *    inside the execution of NCR5380_intr(), leading to recursive
  *    calls.
  *
- *  - I've added a function merge_consecutive_buffers() that tries to
- *    merge scatter-gather buffers that are located at consecutive
+ *  - 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
@@ -432,7 +432,6 @@
 }
 
 
-#if 1
 static void free_all_tags( void )
 {
     int target, lun;
@@ -449,58 +448,48 @@
 	}
     }
 }
-#endif
 
 #endif /* SUPPORT_TAGS */
 
 
 /*
- * Function: void merge_consecutive_buffers( Scsi_Cmnd *cmd )
+ * 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 consecutive addresses.
+ *    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.
  */
 
-/* A special issue is when the buffer is exactly at the end of the
- * last physical memory chunk: VTOP would have to calculate the
- * physical address just 1 byte behind the end of physical memory. But
- * it will panic if given this address :-( So we need to avoid calling
- * VTOP on addresses that don't exist. This is done by keeping
- * 'endadr' to be the real end address of the buffer, not one byte
- * more (which would be easier).
- */
-
-static void merge_consecutive_buffers( Scsi_Cmnd *cmd )
+static void merge_contiguous_buffers( Scsi_Cmnd *cmd )
 {
-    unsigned long endadr;
+    unsigned long endaddr;
 #if (NDEBUG & NDEBUG_MERGING)
     unsigned long oldlen = cmd->SCp.this_residual;
     int		  cnt = 1;
 #endif
 
-    for( endadr = VTOP(cmd->SCp.ptr + cmd->SCp.this_residual - 1);
+    for (endaddr = VTOP(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1;
 	 cmd->SCp.buffers_residual &&
-	 VTOP( (cmd->SCp.buffer+1)->address ) == endadr + 1; ) {
+	 VTOP(cmd->SCp.buffer[1].address) == endaddr; ) {
 	
-	MER_PRINTK( "%08lx == %08lx -> merging\n",
-		    VTOP( (cmd->SCp.buffer+1)->address ), endadr );
+	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;
-	endadr += 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 %08lx, new length %08lx\n",
-		    cnt, (long)(cmd->SCp.ptr), cmd->SCp.this_residual );
+	MER_PRINTK("merged %d buffers from %p, new length %08x\n",
+		   cnt, cmd->SCp.ptr, cmd->SCp.this_residual);
 #endif
 }
 
@@ -526,9 +515,9 @@
 	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
-	 * consecutive physical addresses.
+	 * contiguous physical addresses.
 	 */
-	merge_consecutive_buffers( cmd );
+	merge_contiguous_buffers( cmd );
     } else {
 	cmd->SCp.buffer = NULL;
 	cmd->SCp.buffers_residual = 0;
@@ -725,16 +714,22 @@
 
 static void NCR5380_print_status (struct Scsi_Host *instance)
 {
-    char pr_bfr[256];
+    char *pr_bfr;
     char *start;
     int len;
 
     NCR_PRINT(NDEBUG_ANY);
     NCR_PRINT_PHASE(NDEBUG_ANY);
 
-    len = NCR5380_proc_info(pr_bfr, &start, 0, sizeof(pr_bfr), HOSTNO, 0);
+    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);
 }
 
 
@@ -753,7 +748,9 @@
 */
 
 #undef SPRINTF
-#define SPRINTF(args...) do { if(pos < buffer + length) pos += sprintf(pos, ## args); } while(0)
+#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);
 static
@@ -885,7 +882,7 @@
 	
 
 #ifndef AUTOSENSE
-    if ((instance->cmd_per_lun > 1) || instance->can_queue > 1)) 
+    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);
@@ -1029,7 +1026,7 @@
      * If we're not in an interrupt, we can call NCR5380_main()
      * unconditionally, because it cannot be already running.
      */
-    if (intr_count > 0)
+    if (intr_count > 0 || ((flags >> 8) & 7) >= 6)
 	queue_main();
     else
 	NCR5380_main();
@@ -1077,6 +1074,8 @@
        '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);
@@ -1135,10 +1134,9 @@
 		     * On failure, we must add the command back to the
 		     *   issue queue so we can keep trying.	
 		     */
-		    DPRINTK(NDEBUG_MAIN | NDEBUG_QUEUES,
-			    "scsi%d: main(): command for target %d lun %d "
-			    "removed from issue_queue\n",
-			    HOSTNO, tmp->target, tmp->lun);
+		    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 
@@ -1169,9 +1167,8 @@
 #endif
 			falcon_dont_release--;
 			restore_flags(flags);
-			DPRINTK(NDEBUG_MAIN | NDEBUG_QUEUES,
-				"scsi%d: main(): select() failed, "
-				"returned to issue_queue\n", HOSTNO);
+			MAIN_PRINTK("scsi%d: main(): select() failed, "
+				    "returned to issue_queue\n", HOSTNO);
 			if (hostdata->connected)
 			    break;
 		    }
@@ -2068,9 +2065,9 @@
 		    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 consecutive physical addresses.
+		     * they are at contiguous physical addresses.
 		     */
-		    merge_consecutive_buffers( cmd );
+		    merge_contiguous_buffers( cmd );
 		    INF_PRINTK("scsi%d: %d bytes and %d buffers left\n",
 			       HOSTNO, cmd->SCp.this_residual,
 			       cmd->SCp.buffers_residual);
@@ -2201,13 +2198,15 @@
 			      "completed\n", HOSTNO, cmd->target, cmd->lun);
 #ifdef SUPPORT_TAGS
 		    cmd_free_tag( cmd );
-		    if (cmd->SCp.Status == QUEUE_FULL) {
+		    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",
@@ -2215,7 +2214,6 @@
 				   ta->nr_allocated);
 			if (ta->queue_size > ta->nr_allocated)
 			    ta->nr_allocated = ta->queue_size;
-			cmd->SCp.Status = BUSY;
 		    }
 #else
 		    hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
@@ -2241,12 +2239,12 @@
 
 		    if (cmd->cmnd[0] != REQUEST_SENSE) 
 			cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); 
-		    else if (cmd->SCp.Status != GOOD)
+		    else if (status_byte(cmd->SCp.Status) != GOOD)
 			cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
 		    
 #ifdef AUTOSENSE
 		    if ((cmd->cmnd[0] != REQUEST_SENSE) && 
-			(cmd->SCp.Status == CHECK_CONDITION)) {
+			(status_byte(cmd->SCp.Status) == CHECK_CONDITION)) {
 			ASEN_PRINTK("scsi%d: performing request sense\n",
 				    HOSTNO);
 			cmd->cmnd[0] = REQUEST_SENSE;
@@ -2255,11 +2253,15 @@
 			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->SCp.ptr = (char *) cmd->sense_buffer;
-			cmd->SCp.this_residual = sizeof(cmd->sense_buffer);
+			*/
+			cmd->request_buffer = (char *) cmd->sense_buffer;
+			cmd->request_bufflen = sizeof(cmd->sense_buffer);
 
 			save_flags(flags);
 			cli();
@@ -2692,10 +2694,8 @@
  */
 
     if (hostdata->connected == cmd) {
-	int rv;
 
 	ABRT_PRINTK("scsi%d: aborting connected command\n", HOSTNO);
-	hostdata->aborted = 1;
 /*
  * We should perform BSY checking, and make sure we haven't slipped
  * into BUS FREE.
@@ -2709,31 +2709,29 @@
  */
 
 /* 
- * MSch: use the do_abort function instead ... unless there is need to wait
- * a longer period for the target to go into MSGOUT.
- */
-	rv = do_abort(instance);
-
-	save_flags(flags);
-	cli();
+ * Return control to the executing NCR drive so we can clear the
+ * aborted flag and get back into our main loop.
+ */ 
 
-	cmd->result = DID_ABORT << 16; 
+	if (do_abort(instance) == 0) {
+	  hostdata->aborted = 1;
+	  hostdata->connected = NULL;
+	  cmd->result = DID_ABORT << 16;
 #ifdef SUPPORT_TAGS
-	cmd_free_tag( cmd );
+	  cmd_free_tag( cmd );
 #else
-	hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+	  hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
 #endif
-	restore_flags(flags);
-	cmd->done(cmd);
-	falcon_release_lock_if_possible( hostdata );
-
-/* 
- * Return control to the executing NCR drive so we can clear the
- * aborted flag and get back into our main loop.
- */ 
- 
-	return rv ? SCSI_ABORT_SUCCESS : SCSI_ABORT_ERROR;
-    }
+	  restore_flags(flags);
+	  cmd->scsi_done(cmd);
+	  falcon_release_lock_if_possible( hostdata );
+	  return SCSI_ABORT_SUCCESS;
+	} else {
+/*	  restore_flags(flags); */
+	  printk("scsi%d: abort of connected command failed!\n", HOSTNO);
+	  return SCSI_ABORT_ERROR;
+	} 
+   }
 #endif
 
 /* 
@@ -2753,7 +2751,7 @@
 			HOSTNO);
 	    /* Tagged queuing note: no tag to free here, hasn't been assigned
 	     * yet... */
-	    tmp->done(tmp);
+	    tmp->scsi_done(tmp);
 	    falcon_release_lock_if_possible( hostdata );
 	    return SCSI_ABORT_SUCCESS;
 	}
@@ -2833,7 +2831,7 @@
 		    hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
 #endif
 		    restore_flags(flags);
-		    tmp->done(tmp);
+		    tmp->scsi_done(tmp);
 		    falcon_release_lock_if_possible( hostdata );
 		    return SCSI_ABORT_SUCCESS;
 		}
@@ -2877,7 +2875,9 @@
     SETUP_HOSTDATA(cmd->host);
     int           i;
     unsigned long flags;
+#if 1
     Scsi_Cmnd *connected, *disconnected_queue;
+#endif
 
     if (!IS_A_TT() && !falcon_got_lock)
 	printk(KERN_ERR "scsi%d: !!BINGO!! Falcon has no lock in NCR5380_reset\n",
@@ -2900,7 +2900,10 @@
      * through anymore ... */
     (void)NCR5380_read( RESET_PARITY_INTERRUPT_REG );
 
-#if 1 /* XXX Should now be done by midlevel code, bug isn't XXX */
+#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
@@ -2923,6 +2926,11 @@
 #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);
@@ -2945,9 +2953,61 @@
  */
 /*    falcon_release_lock_if_possible( hostdata );*/
 
-#endif /* 1 */
+    /* 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));
 
-    return SCSI_RESET_WAKEUP;
+    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: */
diff -urN pc-2.0.21/drivers/scsi/atari_scsi.c linux-2.0.21/drivers/scsi/atari_scsi.c
--- pc-2.0.21/drivers/scsi/atari_scsi.c	Mon Jun  3 11:29:12 1996
+++ linux-2.0.21/drivers/scsi/atari_scsi.c	Mon Sep 16 23:54:00 1996
@@ -87,9 +87,10 @@
 #include <linux/delay.h>
 #include <linux/mm.h>
 #include <linux/blk.h>
+#include <linux/sched.h>
 #include <linux/interrupt.h>
 
-#include <asm/bootinfo.h>
+#include <asm/setup.h>
 #include <asm/atarihw.h>
 #include <asm/atariints.h>
 #include <asm/page.h>
@@ -197,8 +198,8 @@
 static unsigned long atari_dma_xfer_len( unsigned long wanted_len,
                                          Scsi_Cmnd *cmd, int write_flag );
 #endif
-static void scsi_tt_intr( int irq, struct pt_regs *fp, void *dummy);
-static void scsi_falcon_intr( int irq, struct pt_regs *fp, void *dummy);
+static void scsi_tt_intr( int irq, void *dummy, struct pt_regs *fp);
+static void scsi_falcon_intr( int irq, void *dummy, struct pt_regs *fp);
 static void falcon_release_lock_if_possible( struct NCR5380_hostdata *
                                              hostdata );
 static void falcon_get_lock( void );
@@ -277,7 +278,7 @@
  * end-of-DMA, both SCSI ints are triggered simultaneously, so the NCR int has
  * to clear the DMA int pending bit before it allows other level 6 interrupts.
  */
-static void scsi_dma_buserr (int irq, struct pt_regs *fp, void *dummy)
+static void scsi_dma_buserr (int irq, void *dummy, struct pt_regs *fp)
 {
 	unsigned char	dma_stat = tt_scsi_dma.dma_ctrl;
 
@@ -306,7 +307,7 @@
 #endif
 
 
-static void scsi_tt_intr (int irq, struct pt_regs *fp, void *dummy)
+static void scsi_tt_intr (int irq, void *dummy, struct pt_regs *fp)
 {
 #ifdef REAL_DMA
 	int dma_stat;
@@ -397,7 +398,7 @@
 }
 
 
-static void scsi_falcon_intr (int irq, struct pt_regs *fp, void *dummy)
+static void scsi_falcon_intr (int irq, void *dummy, struct pt_regs *fp)
 {
 #ifdef REAL_DMA
 	int dma_stat;
@@ -685,8 +686,8 @@
 		/* This int is actually "pseudo-slow", i.e. it acts like a slow
 		 * interrupt after having cleared the pending flag for the DMA
 		 * interrupt. */
-		add_isr(IRQ_TT_MFP_SCSI, scsi_tt_intr, IRQ_TYPE_SLOW,
-			NULL, "SCSI NCR5380");
+		request_irq(IRQ_TT_MFP_SCSI, scsi_tt_intr, IRQ_TYPE_SLOW,
+		            "SCSI NCR5380", scsi_tt_intr);
 		tt_mfp.active_edge |= 0x80;		/* SCSI int on L->H */
 #ifdef REAL_DMA
 		tt_scsi_dma.dma_ctrl = 0;
@@ -747,7 +748,7 @@
 int atari_scsi_release (struct Scsi_Host *sh)
 {
 	if (IS_A_TT())
-		remove_isr (IRQ_TT_MFP_SCSI, scsi_tt_intr, NULL);
+		free_irq(IRQ_TT_MFP_SCSI, scsi_tt_intr);
 	if (atari_dma_buffer)
 		scsi_init_free (atari_dma_buffer, STRAM_BUFFER_SIZE);
 	return 1;
@@ -851,7 +852,8 @@
 	else {
 		atari_turnon_irq( IRQ_MFP_FSCSI );
 	}
-	falcon_release_lock_if_possible(hostdata);
+	if ((rv & SCSI_RESET_ACTION) == SCSI_RESET_SUCCESS)
+		falcon_release_lock_if_possible(hostdata);
 
 	return( rv );
 }
@@ -1085,8 +1087,9 @@
 	if (possible_len > limit)
 		possible_len = limit;
 
-	DMA_PRINTK("Sorry, must cut DMA transfer size to %ld bytes instead "
-		   "of %ld\n", possible_len, wanted_len);
+	if (possible_len != wanted_len)
+		DMA_PRINTK("Sorry, must cut DMA transfer size to %ld bytes "
+			   "instead of %ld\n", possible_len, wanted_len);
 
 	return( possible_len );
 }
diff -urN pc-2.0.21/drivers/scsi/atari_scsi.h linux-2.0.21/drivers/scsi/atari_scsi.h
--- pc-2.0.21/drivers/scsi/atari_scsi.h	Mon May 20 06:54:28 1996
+++ linux-2.0.21/drivers/scsi/atari_scsi.h	Sat Aug 24 14:38:31 1996
@@ -34,7 +34,7 @@
  * values should work, too; try it! (but cmd_per_lun costs memory!) */
 
 /* But there seems to be a bug somewhere that requires CAN_QUEUE to be
- * 2*CMD_OER_LUN. At least on a TT, no spurious timeouts seen since
+ * 2*CMD_PER_LUN. At least on a TT, no spurious timeouts seen since
  * changed CMD_PER_LUN... */
 
 /* Note: The Falcon currently uses 8/1 setting due to unsolved problems with
@@ -93,8 +93,6 @@
 	atari_dma_xfer_len(cmd->SCp.this_residual,cmd,((phase) & SR_IO) ? 0 : 1)
 
 /* Debugging printk definitions:
- * DPRINTK() is the generic one, takes an NDEBUG_* mask as argument;
- * all others are hardcoded to one NDEBUG_* code:
  *
  *  ARB  -> arbitration
  *  ASEN -> auto-sense
@@ -122,58 +120,144 @@
  *
  */
 
-#define DPRINTK(mask, format, args...)				\
-	do {							\
-		if (NDEBUG & (mask))				\
-			printk(KERN_DEBUG format , ## args);	\
-	} while(0)
-
+#if NDEBUG & NDEBUG_ARBITRATION
 #define ARB_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_ARBITRATION, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define ARB_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_AUTOSENSE
 #define ASEN_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_AUTOSENSE, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define ASEN_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_DMA
 #define DMA_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_DMA, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define DMA_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_HANDSHAKE
 #define HSH_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_HANDSHAKE, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define HSH_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INFORMATION
 #define INF_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_INFORMATION, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define INF_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INIT
 #define INI_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_INIT, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define INI_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_INTR
 #define INT_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_INTR, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define INT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_LINKED
 #define LNK_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_LINKED, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define LNK_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_MAIN
 #define MAIN_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_MAIN, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define MAIN_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_NO_DATAOUT
 #define NDAT_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_NO_DATAOUT, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define NDAT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_NO_WRITE
 #define NWR_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_NO_WRITE, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define NWR_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_PIO
 #define PIO_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_PIO, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define PIO_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_PSEUDO_DMA
 #define PDMA_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_PSEUDO_DMA, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define PDMA_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_QUEUES
 #define QU_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_QUEUES, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define QU_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_RESELECTION
 #define RSL_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_RESELECTION, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define RSL_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_SELECTION
 #define SEL_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_SELECTION, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define SEL_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_USLEEP
 #define USL_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_USLEEP, 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...) \
-	DPRINTK(NDEBUG_LAST_BYTE_SENT, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define LBS_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_RESTART_SELECT
 #define RSS_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_RESTART_SELECT, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define RSS_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_EXTENDED
 #define EXT_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_EXTENDED, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define EXT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_ABORT
 #define ABRT_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_ABORT, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define ABRT_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_TAGS
 #define TAG_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_TAGS, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define TAG_PRINTK(format, args...)
+#endif
+#if NDEBUG & NDEBUG_MERGING
 #define MER_PRINTK(format, args...) \
-	DPRINTK(NDEBUG_MERGING, format , ##args)
+	printk(KERN_DEBUG format , ## args)
+#else
+#define MER_PRINTK(format, args...)
+#endif
 
 /* conditional macros for NCR5380_print_{,phase,status} */
 
diff -urN pc-2.0.21/drivers/scsi/cyber_esp.c linux-2.0.21/drivers/scsi/cyber_esp.c
--- pc-2.0.21/drivers/scsi/cyber_esp.c	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/scsi/cyber_esp.c	Sun Sep  8 16:25:57 1996
@@ -0,0 +1,331 @@
+/* cyber_esp.c: Driver for CyberStorm SCSI
+ *
+ * Copyright (C) 1996 Jesper Skov (jskov@iesd.auc.dk)
+ *
+ * The CyberStorm SCSI driver is based on David S. Miller's ESP driver
+ * for the Sparc computers. 
+ * 
+ * This work was made possible by Phase5 who willingly (and most generously)
+ * supported me with hardware and all the information I needed.
+ */
+
+/* TODO:
+ *
+ * 1) Figure out how to make a cleaner merge with the sparc driver with regard
+ *    to the caches and the Sparc MMU mapping.
+ * 2) General clean up of the code. The eps.c code should be renamed to
+ *    NCR53C9X.c and be updated accordingly (i.e. should be more generic).
+ * 3) Make as few routines required outside the generic driver. A lot of the
+ *    routines in this file used to be inline!
+ * 4) Is esp_bytes_sent() correct?
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/blk.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "esp.h"
+#include "cyber_esp.h"
+
+#include <asm/zorro.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+
+#include <asm/pgtable.h>
+
+int  cyber_dma_bytes_sent(struct Sparc_ESP *, int);
+int  cyber_dma_can_transfer(struct Sparc_ESP *, Scsi_Cmnd *);
+void cyber_dma_dump_state(struct Sparc_ESP *);
+void cyber_dma_init_read(struct Sparc_ESP *, char *, int);
+void cyber_dma_init_write(struct Sparc_ESP *, char *, int);
+void cyber_dma_ints_off(struct Sparc_ESP *esp);
+void cyber_dma_ints_on(struct Sparc_ESP *esp);
+int  cyber_dma_irq_p(struct Sparc_ESP *);
+void cyber_dma_led_off(struct Sparc_ESP *);
+void cyber_dma_led_on(struct Sparc_ESP *);
+void cyber_dma_poll(struct Sparc_ESP *, unsigned char *);
+int  cyber_dma_ports_p(struct Sparc_ESP *esp);
+void cyber_dma_reset(struct Sparc_ESP *);
+void cyber_dma_setup(struct Sparc_ESP *, char *, int, int);
+
+unsigned char ctrl_data = 0;	/* Keep backup of the stuff written
+				 * to ctrl_reg. Always write a copy
+				 * to this register when writing to
+				 * the hardware register!
+				 */
+
+volatile unsigned char esp_dma_block[16+16];
+				/* This is where all commands are put
+				 * before they are trasfered to the ESP chip
+				 * via DMA. This block should be 16 bytes
+				 * aligned (so cache flush can be done
+				 * by pushing a single line in the cache).
+				 * - 
+				 * This is now accomplished by adding another
+				 * 16 bytes to the array, and aligning the
+				 * esp->command pointer correctly in
+				 * esp_detect().
+				 */
+
+/***************************************************************** Detection */
+int cyber_esp_detect(Scsi_Host_Template *tpnt)
+{
+	struct Sparc_ESP *esp;
+	struct ConfigDev *esp_dev;
+	int key;
+	unsigned long address;
+
+	if ((key = zorro_find(MANUF_PHASE5, PROD_CYBERSTORM_SCSI, 0, 0))){
+		esp_dev = zorro_get_board(key);
+		esp = esp_allocate(tpnt, 40000000, (void *) esp_dev);
+
+		/* Required functions */
+		esp->dma_bytes_sent = &cyber_dma_bytes_sent;
+		esp->dma_can_transfer = &cyber_dma_can_transfer;
+		esp->dma_dump_state = &cyber_dma_dump_state;
+		esp->dma_init_read = &cyber_dma_init_read;
+		esp->dma_init_write = &cyber_dma_init_write;
+		esp->dma_ints_off = &cyber_dma_ints_off;
+		esp->dma_ints_on = &cyber_dma_ints_on;
+		esp->dma_irq_p = &cyber_dma_irq_p;
+		esp->dma_ports_p = &cyber_dma_ports_p;
+		esp->dma_setup = &cyber_dma_setup;
+
+		/* Optional functions */
+		esp->dma_barrier = 0;
+		esp->dma_drain = 0;
+		esp->dma_invalidate = 0;
+		esp->dma_irq_entry = 0;
+		esp->dma_irq_exit = 0;
+		esp->dma_led_on = &cyber_dma_led_on;
+		esp->dma_led_off = &cyber_dma_led_off;
+		esp->dma_poll = &cyber_dma_poll;
+		esp->dma_reset = 0;
+
+		/* The DMA registers on the CyberStorm are mapped
+		 * relative to the device (i.e. in the same Zorro
+		 * I/O block).
+		 */
+		address = (unsigned long)ZTWO_VADDR(esp_dev->cd_BoardAddr);
+		esp->dregs = (void *)(address + CYBER_DMA_ADDR);
+
+		/* ESP register base */
+		esp->eregs = (struct ESP_regs *)(address + CYBER_ESP_ADDR);
+		
+		/* Make the DMA command buffer 16 bytes aligned so
+		 * only one cache line has to be cleared.
+		 */
+		esp->esp_command = (volatile unsigned char*)
+			(((unsigned long) esp_dma_block+0x0f) & ~0x0f);
+
+		esp->irq = IRQ_AMIGA_PORTS;
+		request_irq(IRQ_AMIGA_PORTS, esp_intr, 0, "CyberStorm SCSI", esp_intr);
+		/* Figure out our scsi ID on the bus */
+		/* The DMA cond flag contains a hardcoded jumper bit
+		 * which can be used to select host number 6 or 7.
+		 * However, even though it may change, we use a hardcoded
+		 * value of 7.
+		 */
+		esp->scsi_id = 7;
+		esp->scsi_id_mask = (1 << esp->scsi_id);
+		
+		/* Check for differential SCSI-bus */
+		/* What is this stuff? */
+		esp->diff = 0;
+
+		esp_initialize(esp);
+
+		zorro_config_board(key, 0);
+
+		printk("\nESP: Total of %d ESP hosts found, %d actually in use.\n", nesps,esps_in_use);
+		esps_running = esps_in_use;
+		return esps_in_use;
+	}
+	printk("No CyberStorm SCSI controller found!\n");
+	return 0;
+}
+
+/************************************************************* DMA Functions */
+int cyber_dma_bytes_sent(struct Sparc_ESP *esp, int fifo_count)
+{
+	/* Since the CyberStorm DMA is fully dedicated to the ESP chip,
+	 * the number of bytes sent (to the ESP chip) equals the number
+	 * of bytes in the FIFO - there is no buffering in the DMA controller.
+	 * XXXX Do I read this right? It is from host to ESP, right?
+	 */
+	return fifo_count;
+}
+
+int cyber_dma_can_transfer(struct Sparc_ESP *esp, Scsi_Cmnd *sp)
+{
+	/* I don't think there's any limit on the CyberDMA. So we use what
+	 * the ESP chip can handle (24 bit).
+	 */
+	unsigned long sz = sp->SCp.this_residual;
+	if(sz > 0x1000000)
+		sz = 0x1000000;
+	return sz;
+}
+
+void cyber_dma_dump_state(struct Sparc_ESP *esp)
+{
+	ESPLOG(("esp%d: dma -- cond_reg<%02x>\n",
+		esp->esp_id, ((struct cyber_dma_registers *)
+			      (esp->dregs))->cond_reg));
+	ESPLOG(("intreq:<%04x>, intena:<%04x>\n",
+		custom.intreqr, custom.intenar));
+}
+
+void cyber_dma_init_read(struct Sparc_ESP *esp, char *vaddress, int length)
+{
+	struct cyber_dma_registers *dregs = 
+		(struct cyber_dma_registers *) esp->dregs;
+	unsigned long addr = VTOP((unsigned long) vaddress);
+	CACHEINV(addr, length);
+#if 1
+	/* Skimming the files in SCSI have lead me to the (wrong?) conclusion
+	 * that no one expects the low-level SCSI drivers to transfer data
+	 * to or from odd addresses... So I skip this part until I learn
+	 * otherwise.
+	 * An equivalent of the dma_start and dma_stop handling from a3000.c
+	 * would have to be performed in esp_do_data and esp_do_data_finale.
+	 */
+	if(addr & 1){
+		printk("DMA makes odd access in cyber_esp.c\n");
+		printk("Please notify jskov@iesd.auc.dk about this\n");
+		panic("In dma_init_read accessing %08x", (unsigned int) addr);
+	}
+#endif
+	addr &= ~(1);
+	dregs->dma_addr0 = (addr >> 24) & 0xff;
+	dregs->dma_addr1 = (addr >> 16) & 0xff;
+	dregs->dma_addr2 = (addr >>  8) & 0xff;
+	dregs->dma_addr3 = (addr      ) & 0xff;
+	ctrl_data &= ~(CYBER_DMA_WRITE);
+
+	/* Check if physical address is outside Z2 space and of
+	 * block length/block aligned in memory. If this is the
+	 * case, enable 32 bit transfer. In all other cases, fall back
+	 * to 16 bit transfer.
+	 * Obviously 32 bit transfer should be enabled if the DMA address
+	 * and length are 32 bit aligned. However, this leads to some
+	 * strange behavior. Even 64 bit aligned addr/length fails.
+	 * Until I've found a reason for this, 32 bit transfer is only
+	 * used for full-block transfers (1kB).
+	 *							-jskov
+	 */
+#if 0
+	if((addr & 0x3fe) || length & 0x3ff || ((addr > 0x200000) &&
+						(addr < 0xff0000)))
+		ctrl_data &= ~(CYBER_DMA_Z3);	/* Z2, do 16 bit DMA */
+	else
+		ctrl_data |= CYBER_DMA_Z3; /* CHIP/Z3, do 32 bit DMA */
+#else
+	ctrl_data &= ~(CYBER_DMA_Z3);	/* Z2, do 16 bit DMA */
+#endif
+	dregs->ctrl_reg = ctrl_data;
+}
+
+void cyber_dma_init_write(struct Sparc_ESP *esp, char *vaddress, int length)
+{
+	unsigned long addr = VTOP((unsigned long) vaddress);
+	struct cyber_dma_registers *dregs = 
+		(struct cyber_dma_registers *) esp->dregs;
+#if 1
+	if(addr & 1){
+		printk("DMA makes odd access in cyber_esp.c\n");
+		printk("Please notify jskov@iesd.auc.dk about this\n");
+		panic("In dma_init_write accessing %08x",(unsigned int) addr);
+	}
+#endif
+	CACHEFLUSH(addr, length);
+	addr |= 1;
+	dregs->dma_addr0 = (addr >> 24) & 0xff;
+	dregs->dma_addr1 = (addr >> 16) & 0xff;
+	dregs->dma_addr2 = (addr >>  8) & 0xff;
+	dregs->dma_addr3 = (addr      ) & 0xff;
+	ctrl_data |= CYBER_DMA_WRITE;
+
+	/* See comment above */
+#if 0
+	if((addr & 0x3fe) || length & 0x3ff || ((addr > 0x200000) &&
+						(addr < 0xff0000)))
+		ctrl_data &= ~(CYBER_DMA_Z3);	/* Z2, do 16 bit DMA */
+	else
+		ctrl_data |= CYBER_DMA_Z3; /* CHIP/Z3, do 32 bit DMA */
+#else
+	ctrl_data &= ~(CYBER_DMA_Z3);	/* Z2, do 16 bit DMA */
+#endif
+	dregs->ctrl_reg = ctrl_data;
+}
+
+void cyber_dma_ints_off(struct Sparc_ESP *esp)
+{
+	custom.intena = (IF_PORTS);
+}
+
+void cyber_dma_ints_on(struct Sparc_ESP *esp)
+{
+	custom.intena = (IF_SETCLR | IF_PORTS);
+}
+
+int cyber_dma_irq_p(struct Sparc_ESP *esp)
+{
+	/* It's important to check the DMA IRQ bit in the correct way! */
+	return ((esp->eregs->esp_status & ESP_STAT_INTR) &&
+		((((struct cyber_dma_registers *)(esp->dregs))->cond_reg) &
+		 CYBER_DMA_HNDL_INTR));
+}
+
+void cyber_dma_led_off(struct Sparc_ESP *esp)
+{
+	ctrl_data &= ~CYBER_DMA_LED;
+}
+
+void cyber_dma_led_on(struct Sparc_ESP *esp)
+{
+	ctrl_data |= CYBER_DMA_LED;
+}
+
+void cyber_dma_poll(struct Sparc_ESP *esp, unsigned char *vaddr)
+{
+	unsigned long paddr;
+
+	/* We have to keep invalidating the cache, or the loop
+	 * would hang if the address reads 0xff the first time.
+	 */
+	paddr = VTOP(vaddr);
+	do
+		CACHEINVLINE(paddr);
+	while(*vaddr == 0xff);
+	
+	/* How can we be sure that the second byte was also
+	 * received?
+	 * Doing 16 bit DMA by default! Nothing less than that :)
+	 */
+}
+
+int cyber_dma_ports_p(struct Sparc_ESP *esp)
+{
+	return ((custom.intenar) & IF_PORTS);
+}
+
+void cyber_dma_setup(struct Sparc_ESP *esp, char *addr, int count, int write)
+{
+	/* On the Sparc, DMA_ST_WRITE means "move data from device to memory"
+	 * so when (write) is true, it actually means READ!
+	 */
+	if(write){
+		cyber_dma_init_read(esp, addr, count);
+	} else {
+		cyber_dma_init_write(esp, addr, count);
+	}
+}
diff -urN pc-2.0.21/drivers/scsi/cyber_esp.h linux-2.0.21/drivers/scsi/cyber_esp.h
--- pc-2.0.21/drivers/scsi/cyber_esp.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/drivers/scsi/cyber_esp.h	Sun Sep  8 16:25:58 1996
@@ -0,0 +1,87 @@
+/* cyber_esp.h: Defines and structures for the CyberStorm SCSI driver.
+ *
+ * Copyright (C) 1996 Jesper Skov (jskov@iesd.auc.dk)
+ */
+
+#include "esp.h"
+
+#ifndef CYBER_ESP_H
+#define CYBER_ESP_H
+
+/* The controller registers can be found in the Z2 config area at these
+ * offsets:
+ */
+#define CYBER_ESP_ADDR 0xf400
+#define CYBER_DMA_ADDR 0xf800
+
+
+/* The CyberStorm DMA interface */
+struct cyber_dma_registers {
+	volatile unsigned char dma_addr0;	/* DMA address (MSB) [0x000] */
+	unsigned char dmapad1[1];
+	volatile unsigned char dma_addr1;	/* DMA address       [0x002] */
+	unsigned char dmapad2[1];
+	volatile unsigned char dma_addr2;	/* DMA address       [0x004] */
+	unsigned char dmapad3[1];
+	volatile unsigned char dma_addr3;	/* DMA address (LSB) [0x006] */
+	unsigned char dmapad4[0x3fb];
+	volatile unsigned char cond_reg;        /* DMA cond    (ro)  [0x402] */
+#define ctrl_reg  cond_reg			/* DMA control (wo)  [0x402] */
+};
+
+/* DMA control bits */
+#define CYBER_DMA_LED    0x80	/* HD led control 1 = on */
+#define CYBER_DMA_WRITE  0x40	/* DMA direction. 1 = write */
+#define CYBER_DMA_Z3     0x20	/* 16 (Z2) or 32 (CHIP/Z3) bit DMA transfer */
+
+/* DMA status bits */
+#define CYBER_DMA_HNDL_INTR 0x80	/* DMA IRQ pending? */
+
+/* The bits below appears to be Phase5 Debug bits only; they were not
+ * described by Phase5 so using them may seem a bit stupid...
+ */
+#define CYBER_HOST_ID 0x02	/* If set, host ID should be 7, otherwise
+				 * it should be 6.
+				 */
+#define CYBER_SLOW_CABLE 0x08	/* If *not* set, assume SLOW_CABLE */
+
+
+/* CACHE control */
+#if !defined(CACHEFLUSH)
+#define CACHEFLUSH(paddr, length) cache_push(paddr, length)
+#define CACHEINV(paddr, length) cache_clear(paddr, length)
+#define CACHEFLUSHLINE(paddr) cache_push(paddr, 16)
+#define CACHEINVLINE(paddr) cache_clear(paddr, 16)
+#endif
+
+extern int cyber_esp_detect(struct SHT *);
+extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int esp_abort(Scsi_Cmnd *);
+extern int esp_reset(Scsi_Cmnd *, unsigned int);
+
+
+#define SCSI_CYBER_ESP {                                                               \
+/* struct SHT *next */                                         NULL,                   \
+/* long *usage_count */                                        NULL,                   \
+/* struct proc_dir_entry *proc_dir */                          &proc_scsi_esp,         \
+/* int (*proc_info)(char *, char **, off_t, int, int, int) */  NULL,                   \
+/* const char *name */                                         "CyberStorm SCSI", \
+/* int detect(struct SHT *) */                                 cyber_esp_detect,  \
+/* int release(struct Scsi_Host *) */                          NULL,                   \
+/* const char *info(struct Scsi_Host *) */                     NULL,               \
+/* int command(Scsi_Cmnd *) */                                 NULL,            \
+/* int queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)) */ esp_queue,              \
+/* int abort(Scsi_Cmnd *) */                                   esp_abort,              \
+/* int reset(Scsi_Cmnd *) */                                   esp_reset,              \
+/* int slave_attach(int, int) */                               NULL,                   \
+/* int bios_param(Disk *, kdev_t, int[]) */                    NULL,                   \
+/* int can_queue */                                            7,                      \
+/* int this_id */                                              7,                      \
+/* short unsigned int sg_tablesize */                          SG_ALL,                 \
+/* short cmd_per_lun */                                        1,                      \
+/* unsigned char present */                                    0,                      \
+/* unsigned unchecked_isa_dma:1 */                             0,                      \
+/* unsigned use_clustering:1 */                                DISABLE_CLUSTERING, }
+
+#endif /* CYBER_ESP_H */
+
diff -urN pc-2.0.21/drivers/scsi/esp.c linux-2.0.21/drivers/scsi/esp.c
--- pc-2.0.21/drivers/scsi/esp.c	Sun Apr 21 11:42:05 1996
+++ linux-2.0.21/drivers/scsi/esp.c	Sun Sep  8 16:25:18 1996
@@ -1,6 +1,16 @@
 /* esp.c:  EnhancedScsiProcessor Sun SCSI driver code.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * Ugly generalization hacks by Jesper Skov.
+ */
+
+/* TODO:
+ *
+ * 1) Maybe disable parity checking in config register one for SCSI1
+ *    targets.
+ * 2) Add support for DMA2 pipelining.
+ * 3) Add tagged queueing.
  */
 
 #include <linux/kernel.h>
@@ -16,42 +26,69 @@
 #include "hosts.h"
 #include "esp.h"
 
+#ifdef CONFIG_CYBERSTORM_SCSI
+#include "cyber_esp.h"
+#endif
+
+#ifdef CONFIG_BLZ2060_SCSI
+#include "blz2060_esp.h"
+#endif
+
+#ifdef CONFIG_AMIGA		/* Used by both CyberStorm and Ferret */
+#include <asm/zorro.h>
+#include <asm/irq.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#endif
+
+#ifdef CONFIG_SCSI_SUNESP
 #include <asm/sbus.h>
 #include <asm/dma.h>
-#include <asm/system.h>
-#include <asm/idprom.h>
 #include <asm/machines.h>
-#include <asm/ptrace.h>
-#include <asm/pgtable.h>
 #include <asm/oplib.h>
 #include <asm/vaddrs.h>
-#include <asm/io.h>
-
-#define DEBUG_ESP
-/* #define DEBUG_ESP_SG */
-
-#if defined(DEBUG_ESP)
-#define ESPLOG(foo)  printk foo
-#else
-#define ESPLOG(foo)
-#endif /* (DEBUG_ESP) */
+#endif
 
-#define INTERNAL_ESP_ERROR \
-        (panic ("Internal ESP driver error in file %s, line %d\n", \
-		__FILE__, __LINE__))
-
-#define INTERNAL_ESP_ERROR_NOPANIC \
-        (printk ("Internal ESP driver error in file %s, line %d\n", \
-		 __FILE__, __LINE__))
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
 
-/* This enum will be expanded when we have sync code written. */
+/* Command phase enumeration. */
 enum {
-	not_issued   = 0x01,  /* Still in the issue_SC queue.          */
-	in_selection = 0x02,  /* ESP is arbitrating, awaiting IRQ      */
-	in_datain    = 0x04,  /* Data is transferring over the bus     */
-	in_dataout   = 0x08,  /* Data is transferring over the bus     */
-	in_status    = 0x10,  /* Awaiting status/msg bytes from target */
-	in_finale    = 0x11,  /* Sent Msg ack, awaiting disconnect     */
+	not_issued    = 0x00,  /* Still in the issue_SC queue.          */
+
+	/* Various forms of selecting a target. */
+#define in_slct_mask    0x10
+	in_slct_norm  = 0x10,  /* ESP is arbitrating, normal selection  */
+	in_slct_stop  = 0x11,  /* ESP will select, then stop with IRQ   */
+	in_slct_msg   = 0x12,  /* select, then send a message           */
+	in_slct_tag   = 0x13,  /* select and send tagged queue msg      */
+	in_slct_sneg  = 0x14,  /* select and acquire sync capabilities  */
+
+	/* Any post selection activity. */
+#define in_phases_mask  0x20
+	in_datain     = 0x20,  /* Data is transferring from the bus     */
+	in_dataout    = 0x21,  /* Data is transferring to the bus       */
+	in_data_done  = 0x22,  /* Last DMA data operation done (maybe)  */
+	in_msgin      = 0x23,  /* Eating message from target            */
+	in_msgincont  = 0x24,  /* Eating more msg bytes from target     */
+	in_msgindone  = 0x25,  /* Deciding to do with what we were sent */
+	in_msgout     = 0x26,  /* Sending message to target             */
+	in_msgoutdone = 0x27,  /* Done sending msg out                  */
+	in_cmdbegin   = 0x28,  /* Sending cmd after abnormal selection  */
+	in_cmdend     = 0x29,  /* Done sending slow cmd                 */
+	in_status     = 0x2a,  /* Was in status phase, finishing cmd    */
+	in_freeing    = 0x2b,  /* freeing the bus for cmd cmplt or disc */
+	in_the_dark   = 0x2c,  /* Don't know what bus phase we are in   */
+
+	/* Special states, ie. not normal bus transitions... */
+#define in_spec_mask    0x80
+	in_abortone   = 0x80,  /* Aborting one command currently        */
+	in_abortall   = 0x81,  /* Blowing away all commands we have     */
+	in_resetdev   = 0x82,  /* SCSI target reset in progress         */
+	in_resetbus   = 0x83,  /* SCSI bus reset in progress            */
+	in_tgterror   = 0x84,  /* Target did something stupid           */
 };
 
 struct proc_dir_entry proc_scsi_esp = {
@@ -59,9 +96,10 @@
 	S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
 
-struct Sparc_ESP *espchain;
+struct Sparc_ESP *espchain = 0;
+int nesps = 0, esps_in_use = 0, esps_running = 0;
 
-static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs);
+void esp_intr(int irq, void *dev_id, struct pt_regs *pregs);
 static void esp_done(struct Sparc_ESP *esp, int error);
 
 /* Debugging routines */
@@ -181,10 +219,129 @@
 		(stepreg == ESP_STEP_SID ? "1BYTE_MSG_SENT" :
 		 (stepreg == ESP_STEP_NCMD ? "NOT_IN_CMD_PHASE" :
 		  (stepreg == ESP_STEP_PPC ? "CMD_BYTES_LOST" :
-		   (stepreg == ESP_STEP_FINI ? "CMD_SENT_OK" :
+		   (stepreg == ESP_STEP_FINI4 ? "CMD_SENT_OK" :
 		    "UNKNOWN"))))));
 }
 
+#ifdef DEBUG_ESP
+static char *phase_string(int phase)
+{
+	switch(phase) {
+	case not_issued:
+		return "UNISSUED";
+	case in_slct_norm:
+		return "SLCTNORM";
+	case in_slct_stop:
+		return "SLCTSTOP";
+	case in_slct_msg:
+		return "SLCTMSG";
+	case in_slct_tag:
+		return "SLCTTAG";
+	case in_slct_sneg:
+		return "SLCTSNEG";
+	case in_datain:
+		return "DATAIN";
+	case in_dataout:
+		return "DATAOUT";
+	case in_data_done:
+		return "DATADONE";
+	case in_msgin:
+		return "MSGIN";
+	case in_msgincont:
+		return "MSGINCONT";
+	case in_msgindone:
+		return "MSGINDONE";
+	case in_msgout:
+		return "MSGOUT";
+	case in_msgoutdone:
+		return "MSGOUTDONE";
+	case in_cmdbegin:
+		return "CMDBEGIN";
+	case in_cmdend:
+		return "CMDEND";
+	case in_status:
+		return "STATUS";
+	case in_freeing:
+		return "FREEING";
+	case in_the_dark:
+		return "CLUELESS";
+	case in_abortone:
+		return "ABORTONE";
+	case in_abortall:
+		return "ABORTALL";
+	case in_resetdev:
+		return "RESETDEV";
+	case in_resetbus:
+		return "RESETBUS";
+	case in_tgterror:
+		return "TGTERROR";
+	default:
+		return "UNKNOWN";
+	};
+}
+#endif
+
+static inline void esp_advance_phase(Scsi_Cmnd *s, int newphase)
+{
+#ifdef DEBUG_STATE_MACHINE
+	ESPLOG(("<%s>", phase_string(newphase)));
+#endif
+	s->SCp.sent_command = s->SCp.phase;
+	s->SCp.phase = newphase;
+}
+
+extern inline void esp_cmd(struct Sparc_ESP *esp, struct ESP_regs *eregs,
+			   unchar cmd)
+{
+#ifdef DEBUG_ESP_CMDS
+	esp->espcmdlog[esp->espcmdent] = cmd;
+	esp->espcmdent = (esp->espcmdent + 1) & 31;
+#endif
+	eregs->esp_cmd = cmd;
+}
+
+/* How we use the various Linux SCSI data structures for operation.
+ *
+ * struct scsi_cmnd:
+ *
+ *   We keep track of the syncronous capabilities of a target
+ *   in the device member, using sync_min_period and
+ *   sync_max_offset.  These are the values we directly write
+ *   into the ESP registers while running a command.  If offset
+ *   is zero the ESP will use asynchronous transfers.
+ *   If the borken flag is set we assume we shouldn't even bother
+ *   trying to negotiate for synchronous transfer as this target
+ *   is really stupid.  If we notice the target is dropping the
+ *   bus, and we have been allowing it to disconnect, we clear
+ *   the disconnect flag.
+ *
+ *   XXX When and how to first set the disconnect flag initially? XXX
+ *
+ *   XXX Tagged commands?  Linked commands, if at all? XXX
+ *
+ *   The scsi_pointer member of a command structure is used for a few
+ *   things.  We hold the scatter list, if any, here first of all.  Since
+ *   we may need to virtually remap some transfers to different addresses
+ *   we keep track of this in the dvma_address member per scatter, before
+ *   we call the completion routine we call the mmu specific unmapping
+ *   routine which is expected to set all the dvma_address fields to NULL
+ *   so that the target driver does not get confused.
+ *   As expected the Status and Message members of the scsi_pointer struct
+ *   hold the information few to us by the target when a command completes.
+ *   The have_data_in field is used as our "save/restore pointers" save area.
+ *   To make scatter and non-scatter work correctly, this value is said
+ *   to be the logic offset from the beggining of this data transfer.  So
+ *   if for example, the target disconnects (legally) in the middle of
+ *   the third scatter list element we will save have_data_in to be the
+ *   (length_of_scatter_element1 + length_of_scatter_element2 +
+ *    how_far_we_got_in_scatter_element3).  This works out perfectly because
+ *   this is what the numbers mean when the target gives us a modify data
+ *   pointers message.
+ *   The phase keeps track of the state of the ESP driver for this particular
+ *   command.  We hold the previous phase in the sent_command field.
+ */
+
+
 /* Manipulation of the ESP command queues.  Thanks to the aha152x driver
  * and its author, Juergen E. Fischer, for the methods used here.
  * Note that these are per-ESP queues, not global queues like
@@ -207,6 +364,16 @@
 	restore_flags(flags);
 }
 
+static inline void prepend_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC)
+{
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	new_SC->host_scribble = (unsigned char *) *SC;
+	*SC = new_SC;
+	restore_flags(flags);
+}
+
 static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd **SC)
 {
 	Scsi_Cmnd *ptr;
@@ -240,289 +407,307 @@
 	return ptr;
 }
 
-static inline void do_pause(unsigned amount)
-{
-	unsigned long the_time = jiffies + amount;
-
-	while(jiffies < the_time)
-		barrier(); /* Not really needed, but... */
-}
+/* Resetting various pieces of the ESP scsi driver chipset */
 
-/* This places the ESP into a known state at boot time. */
-static inline void esp_bootup_reset(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs)
+/* Reset the ESP chip, _not_ the SCSI bus. */
+static inline void esp_reset_esp(struct Sparc_ESP *esp, struct ESP_regs *eregs)
 {
-	struct sparc_dma_registers *dregs = esp->dregs;
-	volatile unchar trash;
-
-	/* Punt the DVMA into a known state. */
-	dregs->cond_reg |= DMA_RST_SCSI;
-	do_pause(100);
-	dregs->cond_reg &= ~(DMA_RST_SCSI);
-	if(esp->dma->revision == dvmarev2)
-		if(esp->erev != esp100)
-			dregs->cond_reg |= DMA_3CLKS;
-	else if(esp->dma->revision == dvmarev3)
-		if(esp->erev == fas236 || esp->erev == fas100a) {
-			dregs->cond_reg &= ~(DMA_3CLKS);
-			dregs->cond_reg |= DMA_2CLKS;
-		}
-	else if(esp->dma->revision == dvmaesc1)
-		dregs->cond_reg |= DMA_ADD_ENABLE;
-	DMA_INTSON(dregs);
+	int family_code, version, i;
+	volatile int trash;
 
 	/* Now reset the ESP chip */
-	eregs->esp_cmd = ESP_CMD_RC;
-	eregs->esp_cmd = (ESP_CMD_NULL | ESP_CMD_DMA);
-	eregs->esp_cmd = (ESP_CMD_NULL | ESP_CMD_DMA); /* borken hardware... */
+
+	esp_cmd(esp, eregs, ESP_CMD_RC);
+	esp_cmd(esp, eregs, ESP_CMD_NULL | ESP_CMD_DMA);
+	esp_cmd(esp, eregs, ESP_CMD_NULL | ESP_CMD_DMA);
 
 	/* Reload the configuration registers */
-	eregs->esp_cfg1  = esp->config1;
 	eregs->esp_cfact = esp->cfact;
 	eregs->esp_stp   = 0;
 	eregs->esp_soff  = 0;
-	eregs->esp_timeo = esp->sync_defp;
-	if(esp->erev == esp100a || esp->erev == esp236)
+	eregs->esp_timeo = esp->neg_defp;
+
+	/* This is the only point at which it is reliable to read
+	 * the ID-code for a fast ESP chip variant.
+	 */
+	if(esp->erev == fast) {
+		version = eregs->esp_uid;
+		family_code = (version & 0xf8) >> 3;
+		if(family_code != 2)
+			esp->erev = fas100a;
+		else
+			esp->erev = fas236;
+		printk("esp%d: FAST chip is %s (family=%d, version=%d)\n",
+		       esp->esp_id,
+		       (esp->erev == fas236) ? "fas236" : "fas100a",
+		       family_code, (version & 7));
+
+
+		esp->max_period = ((35 * esp->ccycle) / 1000);
+		esp->min_period = ((4 * esp->ccycle) / 1000);
+	} else {
+		esp->max_period = ((35 * esp->ccycle) / 1000);
+		esp->min_period = ((5 * esp->ccycle) / 1000);
+	}
+	esp->max_period = (esp->max_period + 3)>>2;
+	esp->min_period = (esp->min_period + 3)>>2;
+
+	eregs->esp_cfg1  = esp->config1;
+	switch(esp->erev) {
+	case esp100:
+		/* nothing to do */
+		break;
+	case esp100a:
+		eregs->esp_cfg2 = esp->config2;
+		break;
+	case esp236:
+		/* Slow 236 */
+		eregs->esp_cfg2 = esp->config2;
+		eregs->esp_cfg3 = esp->config3[0];
+		break;
+	case fas236:
+		/* Fast 236 */
+		eregs->esp_cfg2 = esp->config2;
+		for(i=0; i<8; i++)
+			esp->config3[i] |= ESP_CONFIG3_FCLK;
+		eregs->esp_cfg3 = esp->config3[0];
+		if(esp->diff)
+			esp->radelay = 0;
+		else
+			esp->radelay = 96;
+		break;
+	case fas100a:
+		/* Fast 100a */
 		eregs->esp_cfg2 = esp->config2;
-	if(esp->erev == esp236)
+		for(i=0; i<8; i++)
+			esp->config3[i] |= ESP_CONFIG3_FCLOCK;
 		eregs->esp_cfg3 = esp->config3[0];
+		esp->radelay = 32;
+		break;
+	default:
+		panic("esp: what could it be... I wonder...");
+		break;
+	};
+
 	/* Eat any bitrot in the chip */
 	trash = eregs->esp_intrpt;
+}
+
+/* This places the ESP into a known state at boot time. */
+inline void esp_bootup_reset(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	volatile unchar trash;
+
+	/* Reset the DMA */
+	if(esp->dma_reset)
+		esp->dma_reset(esp);
+
+	/* Reset the ESP */
+	esp_reset_esp(esp, eregs);
 
 	/* Reset the SCSI bus, but tell ESP not to generate an irq */
 	eregs->esp_cfg1 |= ESP_CONFIG1_SRRDISAB;
-	eregs->esp_cmd = ESP_CMD_RS;
-	do_pause(200);
+	esp_cmd(esp, eregs, ESP_CMD_RS);
+	udelay(400);
 	eregs->esp_cfg1 = esp->config1;
 
 	/* Eat any bitrot in the chip and we are done... */
 	trash = eregs->esp_intrpt;
 }
 
-/* Detecting ESP chips on the machine.  This is the simple and easy
- * version.
+/* Allocate structure and insert basic data such as SCSI chip frequency
+ * data and a pointer to the device
  */
-int esp_detect(Scsi_Host_Template *tpnt)
+struct Sparc_ESP* esp_allocate(Scsi_Host_Template *tpnt, unsigned int fmhz,
+			       void *esp_dev)
 {
 	struct Sparc_ESP *esp, *elink;
 	struct Scsi_Host *esp_host;
-	struct linux_sbus *sbus;
-	struct linux_sbus_device *esp_dev, *sbdev_iter;
-	struct Sparc_ESP_regs *eregs;
-	struct sparc_dma_registers *dregs;
-	struct Linux_SBus_DMA *dma, *dlink;
-	unsigned int fmhz;
-	unchar ccf, bsizes, bsizes_more;
-	int nesps = 0;
-	int esp_node;
-
-	espchain = 0;
-	if(!SBus_chain)
-		panic("No SBUS in esp_detect()");
-	for_each_sbus(sbus) {
-		for_each_sbusdev(sbdev_iter, sbus) {
-			/* Is it an esp sbus device? */
-			esp_dev = sbdev_iter;
-			if(strcmp(esp_dev->prom_name, "esp") &&
-			   strcmp(esp_dev->prom_name, "SUNW,esp")) {
-				if(!esp_dev->child ||
-				   strcmp(esp_dev->prom_name, "espdma"))
-					continue; /* nope... */
-				esp_dev = esp_dev->child;
-				if(strcmp(esp_dev->prom_name, "esp") &&
-				   strcmp(esp_dev->prom_name, "SUNW,esp"))
-					continue; /* how can this happen? */
-			}
-			esp_host = scsi_register(tpnt, sizeof(struct Sparc_ESP));
-			if(!esp_host)
-				panic("Cannot register ESP SCSI host");
-			esp = (struct Sparc_ESP *) esp_host->hostdata;
-			if(!esp)
-				panic("No esp in hostdata");
-			esp->ehost = esp_host;
-			esp->edev = esp_dev;
-			/* Put into the chain of esp chips detected */
-			if(espchain) {
-				elink = espchain;
-				while(elink->next) elink = elink->next;
-				elink->next = esp;
-			} else {
-				espchain = esp;
-			}
-			esp->next = 0;
+	unchar ccf;
 
-			/* Get misc. prom information */
-#define ESP_IS_MY_DVMA(esp, dma)  \
-	((esp->edev->my_bus == dma->SBus_dev->my_bus) && \
-         (esp->edev->slot == dma->SBus_dev->slot) && \
-	 (!strcmp(dma->SBus_dev->prom_name, "dma") || \
-	  !strcmp(dma->SBus_dev->prom_name, "espdma")))
-
-			esp_node = esp_dev->prom_node;
-			prom_getstring(esp_node, "name", esp->prom_name,
-				       sizeof(esp->prom_name));
-			esp->prom_node = esp_node;
-			for_each_dvma(dlink) {
-				if(ESP_IS_MY_DVMA(esp, dlink) && !dlink->allocated)
-					break;
-			}
-#undef ESP_IS_MY_DVMA
-			/* If we don't know how to handle the dvma, do not use this device */
-			if(!dlink){
-				printk ("Cannot find dvma for ESP SCSI\n");
-				scsi_unregister (esp_host);
-				continue;
-			}
-			if (dlink->allocated){
-				printk ("esp: can't use my espdma\n");
-				scsi_unregister (esp_host);
-				continue;
-			}
-			dlink->allocated = 1;
-			dma = dlink;
-			esp->dma = dma;
-			esp->dregs = dregs = dma->regs;
-
-			/* Map in the ESP registers from I/O space */
-			prom_apply_sbus_ranges(esp->edev->reg_addrs, 1);
-			esp->eregs = eregs = (struct Sparc_ESP_regs *)
-				sparc_alloc_io(esp->edev->reg_addrs[0].phys_addr, 0,
-					       PAGE_SIZE, "ESP Registers",
-					       esp->edev->reg_addrs[0].which_io, 0x0);
-			if(!eregs)
-				panic("ESP registers unmappable");
-			esp->esp_command =
-				sparc_dvma_malloc(16, "ESP DVMA Cmd Block");
-			if(!esp->esp_command)
-				panic("ESP DVMA transport area unmappable");
-
-			/* Set up the irq's etc. */
-			esp->ehost->base = (unsigned char *) esp->eregs;
-			esp->ehost->io_port = (unsigned int) esp->eregs;
-			esp->ehost->n_io_port = (unsigned char)
-				esp->edev->reg_addrs[0].reg_size;
-			/* XXX The following may be different on sun4ms XXX */
-			esp->ehost->irq = esp->irq = esp->edev->irqs[0].pri;
-
-			/* Allocate the irq only if necessary */
-			for_each_esp(elink) {
-				if((elink != esp) && (esp->irq == elink->irq)) {
-					goto esp_irq_acquired; /* BASIC rulez */
-				}
-			}
-			/* XXX We have shared interrupts per level now, maybe
-			 * XXX use them, maybe not...
-			 */
-			if(request_irq(esp->ehost->irq, esp_intr, SA_INTERRUPT,
-				       "Sparc ESP SCSI", NULL))
-				panic("Cannot acquire ESP irq line");
-esp_irq_acquired:
-			printk("esp%d: IRQ %d ", nesps, esp->ehost->irq);
-			/* Figure out our scsi ID on the bus */
-			esp->scsi_id = prom_getintdefault(esp->prom_node,
-							  "initiator-id", -1);
-			if(esp->scsi_id == -1)
-				esp->scsi_id = prom_getintdefault(esp->prom_node,
-								  "scsi-initiator-id", -1);
-			if(esp->scsi_id == -1)
-				esp->scsi_id =
-					prom_getintdefault(esp->edev->my_bus->prom_node,
-							   "scsi-initiator-id", 7);
-			esp->ehost->this_id = esp->scsi_id;
-			esp->scsi_id_mask = (1 << esp->scsi_id);
-			/* Check for differential bus */
-			esp->diff = prom_getintdefault(esp->prom_node, "differential", -1);
-			esp->diff = (esp->diff == -1) ? 0 : 1;
-			/* Check out the clock properties of the chip */
-			fmhz = prom_getintdefault(esp->prom_node, "clock-frequency", -1);
-			if(fmhz==-1)
-				fmhz = prom_getintdefault(esp->edev->my_bus->prom_node,
-							  "clock-frequency", -1);
-			if(fmhz <= (5000))
-				ccf = 0;
-			else
-				ccf = (((5000 - 1) + (fmhz))/(5000));
-			if(!ccf || ccf > 8) {
-				ccf = ESP_CCF_F4;
-				fmhz = (5000 * 4);
-			}
-			if(ccf==(ESP_CCF_F7+1))
-				esp->cfact = ESP_CCF_F0;
-			else if(ccf == ESP_CCF_NEVER)
-				esp->cfact = ESP_CCF_F2;
-			else
-				esp->cfact = ccf;
-			esp->cfreq = fmhz;
-			esp->ccycle = ((1000000000) / ((fmhz)/1000));
-			esp->ctick = ((7682 * esp->cfact * esp->ccycle)/1000);
-			esp->sync_defp = ((7682 + esp->ctick - 1) / esp->ctick);
-
-			/* XXX HACK HACK HACK XXX */
-			if (esp->sync_defp < 153)
-				esp->sync_defp = 153;
-
-			printk("SCSI ID %d  Clock %d MHz Period %2x ", esp->scsi_id,
-			       (fmhz / 1000), esp->sync_defp);
-
-			/* Find the burst sizes this dma supports. */
-			bsizes = prom_getintdefault(esp->prom_node, "burst-sizes", 0xff);
-			bsizes_more = prom_getintdefault(esp->edev->my_bus->prom_node,
-							 "burst-sizes", 0xff);
-			if(bsizes_more != 0xff) bsizes &= bsizes_more;
-			if(bsizes == 0xff || (bsizes & DMA_BURST16)==0 ||
-			   (bsizes & DMA_BURST32)==0)
-				bsizes = (DMA_BURST32 - 1);
-			esp->bursts = bsizes;
-
-			/* Probe the revision of this esp */
-			esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
-			esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
-			esp->config3[0] = ESP_CONFIG3_TENB;
-			eregs->esp_cfg2 = esp->config2;
-			if((eregs->esp_cfg2 & ~(ESP_CONFIG2_MAGIC)) !=
-			   (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
-				printk("NCR53C90(esp100) detected\n");
-				esp->erev = esp100;
-			} else {
-				eregs->esp_cfg2 = esp->config2 = 0;
-				eregs->esp_cfg3 = 0;
-				eregs->esp_cfg3 = esp->config3[0] = 5;
-				if(eregs->esp_cfg3 != 5) {
-					printk("NCR53C90A(esp100a) detected\n");
-					esp->erev = esp100a;
-				} else {
-					int target;
+	esp_host = scsi_register(tpnt, sizeof(struct Sparc_ESP));
+	if(!esp_host)
+		panic("Cannot register ESP SCSI host");
+	esp = (struct Sparc_ESP *) esp_host->hostdata;
+	if(!esp)
+		panic("No esp in hostdata");
+	esp->ehost = esp_host;
+	esp->edev = esp_dev;
+	esp->esp_id = nesps++;
+	/* Put into the chain of esp chips detected */
+	if(espchain) {
+		elink = espchain;
+		while(elink->next) elink = elink->next;
+		elink->next = esp;
+	} else {
+		espchain = esp;
+	}
+	esp->next = 0;
 
-					for(target=0; target<8; target++)
-						esp->config3[target] = 0;
-					eregs->esp_cfg3 = 0;
-					if(esp->cfact > ESP_CCF_F5) {
-						printk("NCR53C9XF(espfast) detected\n");
-						esp->erev = fast;
-						esp->config2 |= ESP_CONFIG2_FENAB;
-						eregs->esp_cfg2 = esp->config2;
-					} else {
-						printk("NCR53C9x(esp236) detected\n");
-						esp->erev = esp236;
-						eregs->esp_cfg2 = esp->config2 = 0;
-					}
-				}
-			}				
 
-			/* Initialize the command queues */
-			esp->current_SC = 0;
-			esp->disconnected_SC = 0;
-			esp->issue_SC = 0;
+	/* Check out the clock properties of the chip. */
 
-			/* Reset the thing before we try anything... */
-			esp_bootup_reset(esp, eregs);
+	/* This is getting messy but it has to be done
+	 * correctly or else you get weird behavior all
+	 * over the place.  We are trying to basically
+	 * figure out three pieces of information.
+	 *
+	 * a) Clock Conversion Factor
+	 *
+	 *    This is a representation of the input
+	 *    crystal clock frequency going into the
+	 *    ESP on this machine.  Any operation whose
+	 *    timing is longer than 400ns depends on this
+	 *    value being correct.  For example, you'll
+	 *    get blips for arbitration/selection during
+	 *    high load or with multiple targets if this
+	 *    is not set correctly.
+	 *
+	 * b) Selection Time-Out
+	 *
+	 *    The ESP isn't very bright and will arbitrate
+	 *    for the bus and try to select a target
+	 *    forever if you let it.  This value tells
+	 *    the ESP when it has taken too long to
+	 *    negotiate and that it should interrupt
+	 *    the CPU so we can see what happened.
+	 *    The value is computed as follows (from
+	 *    NCR/Symbios chip docs).
+	 *
+	 *          (Time Out Period) *  (Input Clock)
+	 *    STO = ----------------------------------
+	 *          (8192) * (Clock Conversion Factor)
+	 *
+	 *    You usually want the time out period to be
+	 *    around 250ms, I think we'll set it a little
+	 *    bit higher to account for fully loaded SCSI
+	 *    bus's and slow devices that don't respond so
+	 *    quickly to selection attempts. (yeah, I know
+	 *    this is out of spec. but there is a lot of
+	 *    buggy pieces of firmware out there so bite me)
+	 *
+	 * c) Imperical constants for synchronous offset
+	 *    and transfer period register values
+	 *
+	 *    This entails the smallest and largest sync
+	 *    period we could ever handle on this ESP.
+	 */
+	
+	if(fmhz <= (5000000))
+		ccf = 0;
+	else
+		ccf = (((5000000 - 1) + (fmhz))/(5000000));
+	if(!ccf || ccf > 8) {
+		/* If we can't find anything reasonable,
+		 * just assume 20MHZ.  This is the clock
+		 * frequency of the older sun4c's where I've
+		 * been unable to find the clock-frequency
+		 * PROM property.  All other machines provide
+		 * useful values it seems.
+		 */
+		ccf = ESP_CCF_F4;
+		fmhz = (20000000);
+	}
+	if(ccf==(ESP_CCF_F7+1))
+		esp->cfact = ESP_CCF_F0;
+	else if(ccf == ESP_CCF_NEVER)
+		esp->cfact = ESP_CCF_F2;
+	else
+		esp->cfact = ccf;
+	esp->cfreq = fmhz;
+	esp->ccf = ccf;
+	esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz);
+	esp->ctick = ESP_TICK(ccf, esp->ccycle);
+	esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf);
+	esp->sync_defp = SYNC_DEFP_SLOW;
+
+	return esp;
+}
+
+/* Complete initialization of ESP structure and device
+ * Caller must have initialized appropriate parts of the ESP structure
+ * between the call to esp_allocate and this function.
+ */
+void esp_initialize(struct Sparc_ESP *esp)
+{
+	struct ESP_regs *eregs = esp->eregs;
+	int i;
+	
+	/* Fill in ehost data */
+	esp->ehost->base = (unsigned char *) eregs;
+	esp->ehost->io_port = (unsigned int) eregs;
+	esp->ehost->this_id = esp->scsi_id;
+	esp->ehost->irq = esp->irq;
+
+	/* Probe the revision of this esp */
+	esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
+	esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
+	eregs->esp_cfg2 = esp->config2;
+	if((eregs->esp_cfg2 & ~(ESP_CONFIG2_MAGIC)) !=
+	   (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
+		printk("NCR53C90(esp100) detected\n");
+		esp->erev = esp100;
+	} else {
+		eregs->esp_cfg2 = esp->config2 = 0;
+		eregs->esp_cfg3 = 0;
+		eregs->esp_cfg3 = esp->config3[0] = 5;
+		if(eregs->esp_cfg3 != 5) {
+			printk("NCR53C90A(esp100a) detected\n");
+			esp->erev = esp100a;
+		} else {
+			int target;
+			
+			for(target=0; target<8; target++)
+				esp->config3[target] = 0;
+			eregs->esp_cfg3 = 0;
+			if((esp->ccf) > ESP_CCF_F5) {
+				printk("NCR53C9XF(espfast) detected\n");
+				esp->erev = fast;
+				eregs->esp_cfg2 = esp->config2 = 0;
+				esp->sync_defp = SYNC_DEFP_FAST;
+			} else {
+				printk("NCR53C9x(esp236) detected\n");
+				esp->erev = esp236;
+				eregs->esp_cfg2 = esp->config2 = 0;
+			}
+		}
+	}				
+	
+	/* Initialize the command queues */
+	esp->current_SC = 0;
+	esp->disconnected_SC = 0;
+	esp->issue_SC = 0;
+
+	/* Clear the state machines. */
+	esp->targets_present = 0;
+	esp->being_reselected = 0;
+	esp->resetting_bus = 0;
+	esp->aborting_cmd = 0;
+	esp->aborting_all = 0;
+	esp->snip = 0;
+	esp->targets_present = 0;
+	for(i = 0; i < 32; i++)
+		esp->espcmdlog[i] = 0;
+	esp->espcmdent = 0;
+	for(i = 0; i < 16; i++) {
+		esp->cur_msgout[i] = 0;
+		esp->cur_msgin[i] = 0;
+	}
+	esp->prevmsgout = esp->prevmsgin = 0;
+	esp->msgout_len = esp->msgin_len = 0;
+	
+	/* Reset the thing before we try anything... */
+	esp_bootup_reset(esp, eregs);
+	
+	esps_in_use++;
+
+	printk("SCSI ID %d  Clock %d MHz CCF=%d Time-Out %d ",
+	       esp->scsi_id, (esp->cfreq / 1000000),
+	       esp->ccf, (int) esp->neg_defp);
 
-			nesps++;
 #ifdef THREADED_ESP_DRIVER
-			kernel_thread(esp_kernel_thread, esp, 0);
+	kernel_thread(esp_kernel_thread, esp, 0);
 #endif
-		} /* for each sbusdev */
-	} /* for each sbus */
-	return nesps;
 }
 
 /*
@@ -542,8 +727,6 @@
 		return "Sparc ESP100A (NCR53C90A)";
 	case esp236:
 		return "Sparc ESP236";
-	case fast:
-		return "Sparc ESP-FAST (236 or 100A)";
 	case fas236:
 		return "Sparc ESP236-FAST";
 	case fas100a:
@@ -553,80 +736,241 @@
 	};
 }
 
-/* Execute a SCSI command when the bus is free.   All callers
- * turn off all interrupts, so we don't need to explicitly do
- * it here.
+/* Some rules:
+ *
+ *   1) Never ever panic while something is live on the bus.
+ *      If there is to be any chance of syncing the disks this
+ *      rule is to be obeyed.
+ *
+ *   2) Any target that causes a foul condition will no longer
+ *      have synchronous transfers done to it, no questions
+ *      asked.
+ *
+ *   3) Keep register accesses to a minimum.  Think about some
+ *      day when we have Xbus machines this is running on and
+ *      the ESP chip is on the other end of the machine on a
+ *      different board from the cpu where this is running.
+ */
+
+/* Fire off a command.  We assume the bus is free and that the only
+ * case where we could see an interrupt is where we have disconnected
+ * commands active and they are trying to reselect us.
  */
+static inline void esp_check_cmd(struct Sparc_ESP *esp, Scsi_Cmnd *sp)
+{
+	switch(sp->cmd_len) {
+	case 6:
+	case 10:
+	case 12:
+		esp->esp_slowcmd = 0;
+		break;
+
+	default:
+		esp->esp_slowcmd = 1;
+		esp->esp_scmdleft = sp->cmd_len;
+		esp->esp_scmdp = &sp->cmnd[0];
+		break;
+	};
+}
+
+static inline void build_sync_nego_msg(struct Sparc_ESP *esp, int period, int offset)
+{
+	esp->cur_msgout[0] = EXTENDED_MESSAGE;
+	esp->cur_msgout[1] = 3;
+	esp->cur_msgout[2] = EXTENDED_SDTR;
+	esp->cur_msgout[3] = period;
+	esp->cur_msgout[4] = offset;
+	esp->msgout_len = 5;
+}
+
 static inline void esp_exec_cmd(struct Sparc_ESP *esp)
 {
-	struct sparc_dma_registers *dregs;
-	struct Sparc_ESP_regs *eregs;
+	struct ESP_regs *eregs = esp->eregs;
 	Scsi_Cmnd *SCptr;
+	Scsi_Device *SDptr;
+	volatile unchar *cmdp = esp->esp_command;
+	unsigned char the_esp_command;
+	int lun, target;
 	int i;
 
-	eregs = esp->eregs;
-	dregs = esp->dregs;
+	/* Try to save the register access. */
+	if(esp->disconnected_SC && (esp->dma_irq_p(esp)))
+		return; /* Hold off, we've been reselected. */
 
 	/* Grab first member of the issue queue. */
 	SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC);
+
+	/* Safe to panic here because current_SC is null. */
 	if(!SCptr)
-		goto bad;
-	SCptr->SCp.phase = in_selection;
+		panic("esp: esp_exec_cmd and issue queue is NULL");
 
-	/* NCR docs say:
-	 * 1) Load select/reselect Bus ID register with target ID
-	 * 2) Load select/reselect Timeout Reg with desired value
-	 * 3) Load Synchronous offset register with zero (for
-	 *    asynchronous transfers).
-	 * 4) Load Synchronous Transfer Period register (if
-	 *    synchronous)
-	 * 5) Load FIFO with 6, 10, or 12 byte SCSI command
-	 * 6) Issue SELECTION_WITHOUT_ATTENTION command
-	 *
-	 * They also mention that a DMA NOP command must be issued
-	 * to the SCSI chip under many circumstances, plus it's
-	 * also a good idea to flush out the fifo just in case.
+	SDptr = SCptr->device;
+	lun = SCptr->lun;
+	target = SCptr->target;
+
+	esp->snip = 0;
+	esp->msgout_len = 0;
+
+	/* Send it out whole, or piece by piece?   The ESP
+	 * only knows how to automatically send out 6, 10,
+	 * and 12 byte commands.  I used to think that the
+	 * Linux SCSI code would never throw anything other
+	 * than that to us, but then again there is the
+	 * SCSI generic driver which can send us anything.
 	 */
+	esp_check_cmd(esp, SCptr);
 
-	/* Load zeros into COUNTER via 2 DMA NOP chip commands
-	 * due to flaky implementations of the 53C9x which don't
-	 * get the idea the first time around.
+	/* If arbitration/selection is successful, the ESP will leave
+	 * ATN asserted, causing the target to go into message out
+	 * phase.  The ESP will feed the target the identify and then
+	 * the target can only legally go to one of command,
+	 * datain/out, status, or message in phase, or stay in message
+	 * out phase (should we be trying to send a sync negotiation
+	 * message after the identify).  It is not allowed to drop
+	 * BSY, but some buggy targets do and we check for this
+	 * condition in the selection complete code.  Most of the time
+	 * we'll make the command bytes available to the ESP and it
+	 * will not interrupt us until it finishes command phase, we
+	 * cannot do this for command sizes the ESP does not
+	 * understand and in this case we'll get interrupted right
+	 * when the target goes into command phase.
+	 *
+	 * It is absolutely _illegal_ in the presence of SCSI-2 devices
+	 * to use the ESP select w/o ATN command.  When SCSI-2 devices are
+	 * present on the bus we _must_ always go straight to message out
+	 * phase with an identify message for the target.  Being that
+	 * selection attempts in SCSI-1 w/o ATN was an option, doing SCSI-2
+	 * selections should not confuse SCSI-1 we hope.
 	 */
-	dregs->cond_reg = (DMA_INT_ENAB | DMA_FIFO_INV);
 
-	eregs->esp_tclow = 0;
-	eregs->esp_tcmed = 0;
-	eregs->esp_cmd   = (ESP_CMD_NULL | ESP_CMD_DMA);
+	if(SDptr->sync) {
+		/* this targets sync is known */
+		if(SDptr->disconnect)
+			*cmdp++ = IDENTIFY(1, lun);
+		else
+			*cmdp++ = IDENTIFY(0, lun);
+
+		if(esp->esp_slowcmd) {
+			the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA);
+			esp_advance_phase(SCptr, in_slct_stop);
+		} else {
+			the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA);
+			esp_advance_phase(SCptr, in_slct_norm);
+		}
+	} else if(!(esp->targets_present & (1<<target)) || !(SDptr->disconnect)) {
+		/* After the bootup SCSI code sends both the
+		 * TEST_UNIT_READY and INQUIRY commands we want
+		 * to at least attempt allowing the device to
+		 * disconnect.
+		 */
+		ESPMISC(("esp: Selecting device for first time. target=%d "
+			 "lun=%d\n", target, SCptr->lun));
+		if(!SDptr->borken && !SDptr->disconnect)
+			SDptr->disconnect = 1;
+
+		*cmdp++ = IDENTIFY(0, lun);
+		esp->prevmsgout = NOP;
+		esp_advance_phase(SCptr, in_slct_norm);
+		the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA);
+
+		/* Take no chances... */
+		SDptr->sync_max_offset = 0;
+		SDptr->sync_min_period = 0;
+	} else {
+		int toshiba_cdrom_hwbug_wkaround = 0;
 
-	/* Flush the fifo of excess garbage. */
-	eregs->esp_cmd   = ESP_CMD_FLUSH;
+		/* We've talked to this guy before,
+		 * but never negotiated.. lets try
+		 */
+
+		if(!SDptr->borken) {
+			if((SDptr->type == TYPE_ROM) &&
+			   (!strncmp(SDptr->vendor, "TOSHIBA", 7))) {
+				/* Nice try sucker... */
+				printk("esp%d: Disabling sync for buggy "
+				       "Toshiba CDROM.\n", esp->esp_id);
+				toshiba_cdrom_hwbug_wkaround = 1;
+				build_sync_nego_msg(esp, 0, 0);
+			} else {
+				build_sync_nego_msg(esp, esp->sync_defp, 15);
+			}
+		} else {
+			build_sync_nego_msg(esp, 0, 0);
+		}
+		SDptr->sync = 1;
+		esp->snip = 1;
 
-	/* Load bus-id and timeout values. */
-	eregs->esp_busid = (SCptr->target & 7);
-	eregs->esp_timeo = esp->sync_defp;
+		/* A fix for broken SCSI1 targets, when they disconnect
+		 * they lock up the bus and confuse ESP.  So disallow
+		 * disconnects for SCSI1 targets for now until we
+		 * find a better fix.
+		 *
+		 * Addendum: This is funny, I figured out what was going
+		 *           on.  The blotzed SCSI1 target would disconnect,
+		 *           one of the other SCSI2 targets or both would be
+		 *           disconnected as well.  The SCSI1 target would
+		 *           stay disconnected long enough that we start
+		 *           up a command on one of the SCSI2 targets.  As
+		 *           the ESP is arbitrating for the bus the SCSI1
+		 *           target begins to arbitrate as well to reselect
+		 *           the ESP.  The SCSI1 target refuses to drop it's
+		 *           ID bit on the data bus even though the ESP is
+		 *           at ID 7 and is the obvious winner for any
+		 *           arbitration.  The ESP is a poor sport and refuses
+		 *           to lose arbitration, it will continue indefinately
+		 *           trying to arbitrate for the bus and can only be
+		 *           stopped via a chip reset or SCSI bus reset.
+		 *           Therefore _no_ disconnects for SCSI1 targets
+		 *           thank you very much. ;-)
+		 */
+		if((SDptr->scsi_level < SCSI_2) || toshiba_cdrom_hwbug_wkaround ||
+		   SDptr->borken) {
+			printk("esp%d: Disabling DISCONNECT for target %d lun %d\n",
+			       esp->esp_id, SCptr->target, SCptr->lun);
+			SDptr->disconnect = 0;
+			*cmdp++ = IDENTIFY(0, lun);
+		} else {
+			*cmdp++ = IDENTIFY(1, lun);
+		}
 
-	eregs->esp_soff  = 0; /* This means async transfer... */
-	eregs->esp_stp   = 0;
+		/* ESP fifo is only so big...
+		 * Make this look like a slow command.
+		 */
+		esp->esp_slowcmd = 1;
+		esp->esp_scmdleft = SCptr->cmd_len;
+		esp->esp_scmdp = &SCptr->cmnd[0];
 
-	/* Load FIFO with the actual SCSI command. */
-	for(i=0; i < SCptr->cmd_len; i++)
-		eregs->esp_fdata = SCptr->cmnd[i];
+		the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA);
+		esp_advance_phase(SCptr, in_slct_msg);
+	}
 
-	/* Make sure the dvma forwards the ESP interrupt. */
-	dregs->cond_reg = DMA_INT_ENAB;
+	if(!esp->esp_slowcmd)
+		for(i = 0; i < SCptr->cmd_len; i++)
+			*cmdp++ = SCptr->cmnd[i];
+
+	eregs->esp_busid = (target & 7);
+	eregs->esp_soff = SDptr->sync_max_offset;
+	eregs->esp_stp  = SDptr->sync_min_period;
+	if(esp->erev > esp100a)
+		eregs->esp_cfg3 = esp->config3[target];
 
-	/* Tell ESP to SELECT without asserting ATN. */
-	eregs->esp_cmd = ESP_CMD_SEL;
-	return;
+	i = (cmdp - esp->esp_command);
+
+	/* Set up the DMA and ESP counters */
+	eregs->esp_tclow = i;
+	eregs->esp_tcmed = 0;
 
-bad:
-	panic("esp: daaarrrkk starrr crashesss....");
+	esp->dma_init_write(esp, (char *) esp->esp_command, i);
+
+	/* Tell ESP to "go". */
+	esp_cmd(esp, eregs, the_esp_command);
 }
 
 /* Queue a SCSI command delivered from the mid-level Linux SCSI code. */
 int esp_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
 {
 	struct Sparc_ESP *esp;
+	int don;
 	unsigned long flags;
 
 	save_flags(flags); cli();
@@ -637,40 +981,70 @@
 
 	esp = (struct Sparc_ESP *) SCpnt->host->hostdata;
 
+	if(esp->dma_led_on)
+		esp->dma_led_on(esp);
+
+	don = esp->dma_ports_p(esp);
+	if(don)
+		esp->dma_ints_off(esp);
+
 	/* We use the scratch area. */
+	ESPQUEUE(("esp_queue: target=%d lun=%d ", SCpnt->target, SCpnt->lun));
+	ESPDISC(("N<%02x,%02x>", SCpnt->target, SCpnt->lun));
 	if(!SCpnt->use_sg) {
+		ESPQUEUE(("!use_sg\n"));
 		SCpnt->SCp.this_residual    = SCpnt->request_bufflen;
 		SCpnt->SCp.buffer           =
 			(struct scatterlist *) SCpnt->request_buffer;
 		SCpnt->SCp.buffers_residual = 0;
-		SCpnt->SCp.Status           = CHECK_CONDITION;
-		SCpnt->SCp.Message          = 0;
-		SCpnt->SCp.have_data_in     = 0;
-		SCpnt->SCp.sent_command     = 0;
-		SCpnt->SCp.ptr = mmu_get_scsi_one((char *)SCpnt->SCp.buffer,
-						     SCpnt->SCp.this_residual,
-						     esp->edev->my_bus);
+#ifdef CONFIG_SCSI_SUNESP
+		/* Sneaky. */
+		SCpnt->SCp.have_data_in = (int) SCpnt->SCp.ptr =
+			mmu_get_scsi_one((char *)SCpnt->SCp.buffer,
+					 SCpnt->SCp.this_residual,
+					 ((struct linux_sbus_device *) (esp->edev))->my_bus);
+#else
+		SCpnt->SCp.have_data_in = (int) SCpnt->SCp.ptr =
+			(char *)SCpnt->request_buffer;
+#endif
+
 	} else {
+		ESPQUEUE(("use_sg "));
 #ifdef DEBUG_ESP_SG
-		printk("esp: sglist at %p with %d buffers\n",
-		       SCpnt->buffer, SCpnt->use_sg);
+		printk("esp%d: sglist at %p with %d buffers\n",
+		       esp->esp_id, SCpnt->buffer, SCpnt->use_sg);
 #endif
 		SCpnt->SCp.buffer           = (struct scatterlist *) SCpnt->buffer;
 		SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
 		SCpnt->SCp.this_residual    = SCpnt->SCp.buffer->length;
+#ifdef CONFIG_SCSI_SUNESP
 		mmu_get_scsi_sgl((struct mmu_sglist *) SCpnt->SCp.buffer,
 				 SCpnt->SCp.buffers_residual,
-				 esp->edev->my_bus);
-		SCpnt->SCp.ptr              = (char *) SCpnt->SCp.buffer->alt_address;
+				 ((struct linux_sbus_device *) (esp->edev))->my_bus);
+		SCpnt->SCp.ptr              = (char *) SCpnt->SCp.buffer->dvma_address;
+#else
+		SCpnt->SCp.ptr              = (char *) SCpnt->SCp.buffer->address;
+#endif
 	}
+	SCpnt->SCp.Status           = CHECK_CONDITION;
+	SCpnt->SCp.Message          = 0xff;
+	SCpnt->SCp.sent_command     = 0;
 
 	/* Place into our queue. */
-	append_SC(&esp->issue_SC, SCpnt);
+	if(SCpnt->cmnd[0] == REQUEST_SENSE) {
+		ESPQUEUE(("RQSENSE\n"));
+		prepend_SC(&esp->issue_SC, SCpnt);
+	} else {
+		ESPQUEUE(("\n"));
+		append_SC(&esp->issue_SC, SCpnt);
+	}
 
-	/* Run it now if we can */
-	if(!esp->current_SC)
+	/* Run it now if we can. */
+	if(!esp->current_SC && !esp->resetting_bus)
 		esp_exec_cmd(esp);
 
+	if(don)
+		esp->dma_ints_on(esp);
 	restore_flags(flags);
 	return 0;
 }
@@ -678,17 +1052,152 @@
 /* Only queuing supported in this ESP driver. */
 int esp_command(Scsi_Cmnd *SCpnt)
 {
-	ESPLOG(("esp: esp_command() called...\n"));
+#ifdef DEBUG_ESP
+	struct Sparc_ESP *esp = (struct Sparc_ESP *) SCpnt->host->hostdata;
+#endif
+
+	ESPLOG(("esp%d: esp_command() called...\n", esp->esp_id));
 	return -1;
 }
 
-/* Abort a command.  Those that are on the bus force a SCSI bus
- * reset.
- */
-int esp_abort(Scsi_Cmnd *SCpnt)
+/* Dump driver state. */
+static inline void esp_dump_cmd(Scsi_Cmnd *SCptr)
+{
+	ESPLOG(("[tgt<%02x> lun<%02x> "
+		"pphase<%s> cphase<%s>]",
+		SCptr->target, SCptr->lun,
+		phase_string(SCptr->SCp.sent_command),
+		phase_string(SCptr->SCp.phase)));
+}
+
+static inline void esp_dump_state(struct Sparc_ESP *esp, 
+				  struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+#ifdef DEBUG_ESP_CMDS
+	int i;
+#endif
+
+	ESPLOG(("esp%d: dumping state\n", esp->esp_id));
+	
+	/* Print DMA status */
+	esp->dma_dump_state(esp);
+
+	ESPLOG(("esp%d: SW [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
+		esp->esp_id, esp->sreg, esp->seqreg, esp->ireg));
+	ESPLOG(("esp%d: HW reread [sreg<%02x> sstep<%02x> ireg<%02x>]\n",
+		esp->esp_id, eregs->esp_status, eregs->esp_sstep, eregs->esp_intrpt));
+#ifdef DEBUG_ESP_CMDS
+	printk("esp%d: last ESP cmds [", esp->esp_id);
+	i = (esp->espcmdent - 1) & 31;
+	printk("<");
+	esp_print_cmd(esp->espcmdlog[i]);
+	printk(">");
+	i = (i - 1) & 31;
+	printk("<");
+	esp_print_cmd(esp->espcmdlog[i]);
+	printk(">");
+	i = (i - 1) & 31;
+	printk("<");
+	esp_print_cmd(esp->espcmdlog[i]);
+	printk(">");
+	i = (i - 1) & 31;
+	printk("<");
+	esp_print_cmd(esp->espcmdlog[i]);
+	printk(">");
+	printk("]\n");
+#endif /* (DEBUG_ESP_CMDS) */
+
+	if(SCptr) {
+		ESPLOG(("esp%d: current command ", esp->esp_id));
+		esp_dump_cmd(SCptr);
+	}
+	ESPLOG(("\n"));
+	SCptr = esp->disconnected_SC;
+	ESPLOG(("esp%d: disconnected ", esp->esp_id));
+	while(SCptr) {
+		esp_dump_cmd(SCptr);
+		SCptr = (Scsi_Cmnd *) SCptr->host_scribble;
+	}
+	ESPLOG(("\n"));
+}
+
+/* Abort a command. */
+int esp_abort(Scsi_Cmnd *SCptr)
 {
-	ESPLOG(("esp_abort: Not implemented yet\n"));
-	return SCSI_ABORT_ERROR;
+	struct Sparc_ESP *esp = (struct Sparc_ESP *) SCptr->host->hostdata;
+	struct ESP_regs *eregs = esp->eregs;
+	int don;
+	unsigned long flags;
+
+	ESPLOG(("esp%d: Aborting command\n", esp->esp_id));
+	save_flags(flags); cli();
+
+	esp_dump_state(esp, eregs);
+
+	/* Wheee, if this is the current command on the bus, the
+	 * best we can do is assert ATN and wait for msgout phase.
+	 * This should even fix a hung SCSI bus when we lose state
+	 * in the driver and timeout because the eventual phase change
+	 * will cause the ESP to (eventually) give an interrupt.
+	 */
+	if(esp->current_SC == SCptr) {
+		esp->cur_msgout[0] = ABORT;
+		esp->msgout_len = 1;
+		esp->msgout_ctr = 0;
+		esp_cmd(esp, eregs, ESP_CMD_SATN);
+		restore_flags(flags);
+		return SCSI_ABORT_PENDING;
+	}
+
+	/* If it is still in the issue queue then we can safely
+	 * call the completion routine and report abort success.
+	 */
+	don = esp->dma_ports_p(esp);
+	if(don)
+		esp->dma_ints_off(esp);
+	if(esp->issue_SC) {
+		Scsi_Cmnd **prev, *this;
+		for(prev = (&esp->issue_SC), this = esp->issue_SC;
+		    this;
+		    prev = (Scsi_Cmnd **) &(this->host_scribble),
+		    this = (Scsi_Cmnd *) this->host_scribble) {
+			if(this == SCptr) {
+				*prev = (Scsi_Cmnd *) this->host_scribble;
+				this->host_scribble = NULL;
+				this->result = DID_ABORT << 16;
+				this->done(this);
+				cli();
+				if(don)
+					esp->dma_ints_on(esp);
+				restore_flags(flags);
+				return SCSI_ABORT_SUCCESS;
+			}
+		}
+	}
+
+	/* Yuck, the command to abort is disconnected, it is not
+	 * worth trying to abort it now if something else is live
+	 * on the bus at this time.  So, we let the SCSI code wait
+	 * a little bit and try again later.
+	 */
+	if(esp->current_SC) {
+		if(don)
+			esp->dma_ints_on(esp);
+		restore_flags(flags);
+		return SCSI_ABORT_SNOOZE;
+	}
+
+	/* It's disconnected, we have to reconnect to re-establish
+	 * the nexus and tell the device to abort.
+	 *
+	 * XXX write this code.
+	 */
+
+	if(don)
+		esp->dma_ints_on(esp);
+	restore_flags(flags);
+	return SCSI_ABORT_SNOOZE;
 }
 
 /* Reset ESP chip, reset hanging bus, then kill active and
@@ -696,8 +1205,13 @@
  */
 int esp_reset(Scsi_Cmnd *SCptr, unsigned int how)
 {
-	ESPLOG(("esp_reset: Not implemented yet\n"));
-	return SCSI_RESET_ERROR;
+	struct Sparc_ESP *esp = (struct Sparc_ESP *) SCptr->host->hostdata;
+	struct ESP_regs *eregs = esp->eregs;
+
+	ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id));
+	esp->resetting_bus = 1;
+	esp_cmd(esp, eregs, ESP_CMD_RS);
+	return SCSI_RESET_PENDING;
 }
 
 /* Internal ESP done function. */
@@ -709,39 +1223,62 @@
 	if(esp->current_SC) {
 		/* Critical section... */
 		save_flags(flags); cli();
+
 		done_SC = esp->current_SC;
 		esp->current_SC = NULL;
+
 		/* Free dvma entry. */
 		if(!done_SC->use_sg) {
-			mmu_release_scsi_one(done_SC->SCp.ptr,
-						done_SC->SCp.this_residual,
-						esp->edev->my_bus);
+#ifdef CONFIG_SCSI_SUNESP
+			/* Sneaky. */
+			mmu_release_scsi_one((char *)done_SC->SCp.have_data_in,
+					     done_SC->request_bufflen,
+					     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+#endif
 		} else {
-			struct scatterlist *scl = (struct scatterlist *)done_SC->buffer;
 #ifdef DEBUG_ESP_SG
-			printk("esp: unmapping sg ");
+			printk("esp%d: unmapping sg ", esp->esp_id);
 #endif
+#ifdef CONFIG_SCSI_SUNESP
+			struct scatterlist *scl = (struct scatterlist *)done_SC->buffer;
 			mmu_release_scsi_sgl((struct mmu_sglist *) scl,
 					     done_SC->use_sg - 1,
-					     esp->edev->my_bus);
+					     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+#endif
 #ifdef DEBUG_ESP_SG
 			printk("done.\n");
 #endif
 		}
+
 		done_SC->result = error;
 		if(done_SC->scsi_done)
 			done_SC->scsi_done(done_SC);
 		else
 			panic("esp: esp->current_SC->scsi_done() == NULL");
 
+		/* There is a window of time within the scsi_done() path
+		 * of execution where interrupts are turned back on full
+		 * blast and left that way.  During that time we could
+		 * reconnect to a disconnected command, then we'd bomb
+		 * out below.  We could also end up executing two commands
+		 * at _once_.  ...just so you know why the cli() is here...
+		 */
+
+		cli(); /* GRRR!!! */
+
 		/* Bus is free, issue any commands in the queue. */
-		if(esp->issue_SC)
+		if(esp->issue_SC && !esp->current_SC)
 			esp_exec_cmd(esp);
 
 		restore_flags(flags);
 		/* End of critical section... */
-	} else
+	} else {
+		/* Panic is safe as current_SC is null so we may still
+		 * be able to accept more commands to sync buffers.
+		 */
+		ESPLOG(("panicing\n"));
 		panic("esp: done() called with NULL esp->current_SC");
+	}
 }
 
 #ifdef THREADED_ESP_DRIVER /* planning stage... */
@@ -771,358 +1308,1872 @@
 }
 #endif
 
-/* Read the interrupt status registers on this ESP board */
-static inline void esp_updatesoft(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs)
-{
-	/* Update our software copies of the three ESP status
-	 * registers for this ESP.  Be careful, reading the
-	 * ESP interrupt register clears the status and sequence
-	 * step registers (unlatches them, you get the idea).
-	 * So read the interrupt register last.
-	 */
-
-	esp->seqreg = eregs->esp_sstep;
-	esp->sreg = eregs->esp_status;
-
-	/* Supposedly, the ESP100A and above assert the highest
-	 * bit in the status register if an interrupt is pending.
-	 * I've never seen this work properly, so let's clear it
-	 * manually while we are here.  If I see any esp chips
-	 * for which this bit is reliable I will conditionalize
-	 * this.  However, I don't see what this extra bit can
-	 * buy me with all the tests I'll have to place all over
-	 * the code to actually use it when I 'can'.  Plus the
-	 * 'pending interrupt' condition can more than reliably
-	 * be obtained from the DVMA control register.
-	 *
-	 * "Broken hardware"  -Linus
-	 */
-	esp->sreg &= (~ESP_STAT_INTR);
-	esp->ireg = eregs->esp_intrpt;   /* Must be last or we lose */
-}
+/* Wheee, ESP interrupt engine. */  
 
-/* #define ESP_IRQ_TRACE */
+enum {
+	do_phase_determine, do_reset_bus, do_reset_complete,
+	do_work_bus, do_intr_end,
+};
 
-#ifdef ESP_IRQ_TRACE
-#define ETRACE(foo)  printk foo
-#else
-#define ETRACE(foo)
-#endif
+/* Forward declarations. */
+static int esp_do_phase_determine(struct Sparc_ESP *esp, 
+				  struct ESP_regs *eregs);
+static int esp_do_data_finale(struct Sparc_ESP *esp, struct ESP_regs *eregs);
+static int esp_select_complete(struct Sparc_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_status(struct Sparc_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_msgin(struct Sparc_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_msgindone(struct Sparc_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_msgout(struct Sparc_ESP *esp, struct ESP_regs *eregs);
+static int esp_do_cmdbegin(struct Sparc_ESP *esp, struct ESP_regs *eregs);
 
-static char last_fflags, last_status, last_msg;
 
-/* Main interrupt handler for an esp adapter. */
-static inline void esp_handle(struct Sparc_ESP *esp)
+/* We try to avoid some interrupts by jumping ahead and see if the ESP
+ * has gotten far enough yet.  Hence the following.
+ */
+static inline int skipahead1(struct Sparc_ESP *esp, struct ESP_regs *eregs,
+			     Scsi_Cmnd *scp, int prev_phase, int new_phase)
 {
-	struct sparc_dma_registers *dregs;
-	struct Sparc_ESP_regs *eregs;
-	Scsi_Cmnd *SCptr;
+	if(scp->SCp.sent_command != prev_phase)
+		return 0;
 
-	eregs = esp->eregs;
-	dregs = esp->dregs;
-	SCptr = esp->current_SC;
+	if(esp->dma_irq_p(esp)) {
+		/* Yes, we are able to save an interrupt. */
+		esp->sreg = eregs->esp_status;
+		esp->ireg = eregs->esp_intrpt;
+
+		esp->sreg &= ~(ESP_STAT_INTR);
+		if(!(esp->ireg & ESP_INTR_SR))
+			return 0;
+		else
+			return do_reset_complete;
+	}
+	/* Ho hum, target is taking forever... */
+	scp->SCp.sent_command = new_phase; /* so we don't recurse... */
+	return do_intr_end;
+}
 
-	DMA_IRQ_ENTRY(esp->dma, dregs);
-	esp_updatesoft(esp, eregs);
+static inline int skipahead2(struct Sparc_ESP *esp,
+			     struct ESP_regs *eregs,
+			     Scsi_Cmnd *scp, int prev_phase1, int prev_phase2,
+			     int new_phase)
+{
+	if(scp->SCp.sent_command != prev_phase1 &&
+	   scp->SCp.sent_command != prev_phase2)
+		return 0;
+	if(esp->dma_irq_p(esp)) {
+		/* Yes, we are able to save an interrupt. */
+		esp->sreg = eregs->esp_status;
+		esp->ireg = eregs->esp_intrpt;
+
+		esp->sreg &= ~(ESP_STAT_INTR);
+		if(!(esp->ireg & ESP_INTR_SR))
+			return 0;
+		else
+			return do_reset_complete;
+	}
+	/* Ho hum, target is taking forever... */
+	scp->SCp.sent_command = new_phase; /* so we don't recurse... */
+	return do_intr_end;
+}
 
-	ETRACE(("ESPIRQ: <%2x,%2x,%2x> --> ", esp->ireg, esp->sreg, esp->seqreg));
+/* Misc. esp helper routines. */
+static inline void esp_setcount(struct ESP_regs *eregs, int cnt)
+{
+	eregs->esp_tclow = (cnt & 0xff);
+	eregs->esp_tcmed = ((cnt >> 8) & 0xff);
+}
 
-	/* Check for errors. */
-	if(!SCptr)
-		panic("esp_handle: current_SC == penguin within interrupt!");
+static inline int esp_getcount(struct ESP_regs *eregs)
+{
+	return (((eregs->esp_tclow)&0xff) |
+		(((eregs->esp_tcmed)&0xff) << 8));
+}
+
+static inline int fcount(struct ESP_regs *eregs)
+{
+	return eregs->esp_fflags & ESP_FF_FBYTES;
+}
+
+static inline int fnzero(struct ESP_regs *eregs)
+{
+	return eregs->esp_fflags & ESP_FF_ONOTZERO;
+}
+
+/* XXX speculative nops unnecessary when continuing amidst a data phase
+ * XXX even on esp100!!!  another case of flooding the bus with I/O reg
+ * XXX writes...
+ */
+static inline void esp_maybe_nop(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	if(esp->erev == esp100)
+		esp_cmd(esp, eregs, ESP_CMD_NULL);
+}
+
+static inline int sreg_to_dataphase(unchar sreg)
+{
+	if((sreg & ESP_STAT_PMASK) == ESP_DOP)
+		return in_dataout;
+	else
+		return in_datain;
+}
+
+static inline int sreg_datainp(unchar sreg)
+{
+	return (sreg & ESP_STAT_PMASK) == ESP_DIP;
+}
+
+static inline int sreg_dataoutp(unchar sreg)
+{
+	return (sreg & ESP_STAT_PMASK) == ESP_DOP;
+}
+
+/* The ESP100 when in synchronous data phase, can mistake a long final
+ * REQ pulse from the target as an extra byte, it places whatever is on
+ * the data lines into the fifo.  For now, we will assume when this
+ * happens is that the target is a bit quirky and we don't want to
+ * be talking synchronously to it anyways.  Regardless, we need to
+ * tell the ESP to eat the extraneous byte so that we can proceed
+ * to the next phase.
+ */
+static inline int esp100_sync_hwbug(struct Sparc_ESP *esp,
+				    struct ESP_regs *eregs, Scsi_Cmnd *sp)
+{
+	/* Do not touch this piece of code. */
+	if((!(esp->erev == esp100)) ||
+	   (!(sreg_datainp((esp->sreg = eregs->esp_status)) && !fcount(eregs)) &&
+	    !(sreg_dataoutp(esp->sreg) && !fnzero(eregs)))) {
+		if(sp->SCp.phase == in_dataout)
+			esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+		return 0;
+	} else {
+		/* Async mode for this guy. */
+		build_sync_nego_msg(esp, 0, 0);
+
+		/* Ack the bogus byte, but set ATN first. */
+		esp_cmd(esp, eregs, ESP_CMD_SATN);
+		esp_cmd(esp, eregs, ESP_CMD_MOK);
+
+		/* We may have to sit around for a bit. */
+		esp_advance_phase(sp, in_the_dark);
+		return 1;
+	}
+}
+
+/* This closes the window during a selection with a reselect pending, because
+ * we use DMA for the selection process the FIFO should hold the correct
+ * contents if we get reselected during this process.  So we just need to
+ * ack the possible illegal cmd interrupt pending on the esp100.
+ */
+static inline int esp100_reconnect_hwbug(struct Sparc_ESP *esp,
+					 struct ESP_regs *eregs)
+{
+	volatile unchar junk;
+
+	if(esp->erev != esp100)
+		return 0;
+	junk = eregs->esp_intrpt;
+
+	if(junk & ESP_INTR_SR)
+		return 1;
+	return 0;
+}
+
+/* This verifies the BUSID bits during a reselection so that we know which
+ * target is talking to us.
+ */
+static inline int reconnect_target(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	int it, me = esp->scsi_id_mask, targ = 0;
+
+	if(2 != fcount(eregs))
+		return -1;
+	it = eregs->esp_fdata;
+	if(!(it & me))
+		return -1;
+	it &= ~me;
+	if(it & (it - 1))
+		return -1;
+	while(!(it & 1))
+		targ++, it >>= 1;
+	return targ;
+}
+
+/* This verifies the identify from the target so that we know which lun is
+ * being reconnected.
+ */
+static inline int reconnect_lun(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	int lun;
+
+	if((esp->sreg & ESP_STAT_PMASK) != ESP_MIP)
+		return -1;
+	lun = eregs->esp_fdata;
+	if(esp->sreg & ESP_STAT_PERR)
+		return 0;
+	if((lun & 0x40) || !(lun & 0x80))
+		return -1;
+	return lun & 7;
+}
+
+/* This puts the driver in a state where it can continue a command that
+ * is being continued via reselection.
+ */
+static inline void esp_connect(struct Sparc_ESP *esp, struct ESP_regs *eregs,
+			       Scsi_Cmnd *sp)
+{
+	Scsi_Device *dp = sp->device;
+	eregs->esp_soff = dp->sync_max_offset;
+	eregs->esp_stp  = dp->sync_min_period;
+	if(esp->erev > esp100a)
+		eregs->esp_cfg3 = esp->config3[sp->target];
+	esp->current_SC = sp;
+}
+
+/* This will place the current working command back into the issue queue
+ * if we are to receive a reselection amidst a selection attempt.
+ */
+static inline void esp_reconnect(struct Sparc_ESP *esp, Scsi_Cmnd *sp)
+{
+	if(!esp->disconnected_SC)
+		printk("esp%d: Weird, being reselected but disconnected "
+		       "command queue is empty.\n", esp->esp_id);
+	esp->snip = 0;
+	esp->current_SC = 0;
+	sp->SCp.phase = not_issued;
+	append_SC(&esp->issue_SC, sp);
+}
+
+/* Begin message in phase. */
+static inline int esp_do_msgin(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+	esp_maybe_nop(esp, eregs);
+	esp_cmd(esp, eregs, ESP_CMD_TI);
+	esp->msgin_len = 1;
+	esp->msgin_ctr = 0;
+	esp_advance_phase(esp->current_SC, in_msgindone);
+	return do_work_bus;
+}
+
+/* Determine how many bytes were successfully sent/received by the ESP.
+ */
+static inline void advance_sg(Scsi_Cmnd *sp)
+{
+	++sp->SCp.buffer;
+	--sp->SCp.buffers_residual;
+	sp->SCp.this_residual = sp->SCp.buffer->length;
+#ifdef CONFIG_SCSI_SUNESP
+	sp->SCp.ptr = sp->SCp.buffer->dvma_address;
+#else
+	sp->SCp.ptr = sp->SCp.buffer->address;
+#endif
+}
+
+/* Complete a command, this is the only way to safely finish
+ * a command with disconnected commands or a reset interrupt
+ * pending because scsi_done() can enable interrupts full blast.
+ */
+static inline void safe_esp_done(struct Sparc_ESP *esp, int error)
+{
+	int before = 0;
 
-	/* At this point in time, this esp driver should not see
-	 * scsibus resets, parity errors, or gross errors unless
-	 * something truly terrible happens which we are not ready
-	 * to properly recover from yet.
+	if(esp->dma_ports_p(esp)){
+		before = 1;
+		esp->dma_ints_off(esp);
+	}
+	esp_done(esp, error);
+	if(before)
+		esp->dma_ints_on(esp);
+}
+
+/* Please note that the way I've coded these routines is that I _always_
+ * check for a disconnect during any and all information transfer
+ * phases.  The SCSI standard states that the target _can_ cause a BUS
+ * FREE condition by dropping all MSG/CD/IO/BSY signals.  Also note
+ * that during information transfer phases the target controls every
+ * change in phase, the only thing the initiator can do is "ask" for
+ * a message out phase by driving ATN true.  The target can, and sometimes
+ * will, completely ignore this request so we cannot assume anything when
+ * we try to force a message out phase to abort/reset a target.  Most of
+ * the time the target will eventually be nice and go to message out, so
+ * we may have to hold on to our state about what we want to tell the target
+ * for some period of time.
+ */
+
+/* I think I have things working here correctly.  Even partial transfers
+ * within a buffer or sub-buffer should not upset us at all no matter
+ * how bad the target and/or ESP fucks things up.
+ */
+
+static inline int esp_do_data(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int thisphase, hmuch;
+
+	ESPDATA(("esp_do_data: "));
+	esp_maybe_nop(esp, eregs);
+	thisphase = sreg_to_dataphase(esp->sreg);
+	esp_advance_phase(SCptr, thisphase);
+	ESPDATA(("newphase<%s> ", (thisphase == in_datain) ? "DATAIN" : "DATAOUT"));
+	hmuch = esp->dma_can_transfer(esp, SCptr);
+	ESPDATA(("hmuch<%d> ", hmuch));
+	esp_setcount(eregs, hmuch);
+
+	esp->dma_setup(esp, SCptr->SCp.ptr, hmuch, (thisphase == in_datain));
+	esp->current_transfer_size = hmuch;
+	ESPDATA(("DMA|TI --> do_intr_end\n"));
+	esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+	return do_intr_end;
+}
+
+/* See how successful the data transfer was. */
+static inline int esp_do_data_finale(struct Sparc_ESP *esp,
+				     struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int bogus_data = 0, bytes_sent = 0, fifocnt, ecount;
+
+	if(esp->dma_led_off)
+		esp->dma_led_off(esp);
+
+	ESPDATA(("esp_do_data_finale: "));
+
+	if(SCptr->SCp.phase == in_datain) {
+		if(esp->sreg & ESP_STAT_PERR) {
+			/* Yuck, parity error.  The ESP asserts ATN
+			 * so that we can go to message out phase
+			 * immediately and inform the target that
+			 * something bad happened.
+			 */
+			ESPLOG(("esp%d: data bad parity detected.\n",
+				esp->esp_id));
+			esp->cur_msgout[0] = INITIATOR_ERROR;
+			esp->msgout_len = 1;
+		}
+		if(esp->dma_drain)
+			esp->dma_drain(esp);
+	}
+	if(esp->dma_invalidate)
+		esp->dma_invalidate(esp);
+
+	/* This could happen for the above parity error case. */
+	if(!(esp->ireg == ESP_INTR_BSERV)) {
+		/* Please go to msgout phase, please please please... */
+		ESPLOG(("esp%d: !BSERV after data, probably to msgout\n",
+			esp->esp_id));
+		return esp_do_phase_determine(esp, eregs);
+	}	
+
+	/* Check for partial transfers and other horrible events. */
+	fifocnt = fcount(eregs);
+	ecount = esp_getcount(eregs);
+	bytes_sent = esp->current_transfer_size;
+	if(!(esp->sreg & ESP_STAT_TCNT))
+		bytes_sent -= ecount;
+	if(SCptr->SCp.phase == in_dataout)
+		bytes_sent -= fifocnt;
+
+	ESPDATA(("bytes_sent=%d, ", bytes_sent));
+
+	/* If we were in synchronous mode, check for peculiarities. */
+	if(SCptr->device->sync_max_offset)
+		bogus_data = esp100_sync_hwbug(esp, eregs, SCptr);
+	else
+		esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+
+	/* Until we are sure of what has happened, we are certainly
+	 * in the dark.
 	 */
-	if((esp->ireg & (ESP_INTR_SR | ESP_INTR_IC)) ||
-	   (esp->sreg & (ESP_STAT_PERR | ESP_STAT_SPAM))) {
-		printk("esp: really bad error detected\n");
-		printk("esp: intr<%2x> stat<%2x> seq<%2x>",
-		       esp->ireg, esp->sreg, esp->seqreg);
-		printk("esp: SCptr->SCp.phase = %d\n", SCptr->SCp.phase);
-		panic("esp: cannot continue\n");
+	esp_advance_phase(SCptr, in_the_dark);
+
+	if(bytes_sent < 0) {
+		/* I've seen this happen due to lost state in this
+		 * driver.  No idea why it happened, but allowing
+		 * this value to be negative caused things to
+		 * lock up.  This allows greater chance of recovery.
+		 */
+		ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id));
+		ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n",
+			esp->esp_id,
+			esp->current_transfer_size, fifocnt, ecount));
+		ESPLOG(("esp%d: use_sg=%d ptr=%p this_residual=%d\n",
+			esp->esp_id,
+			SCptr->use_sg, SCptr->SCp.ptr, SCptr->SCp.this_residual));
+		bytes_sent = 0;
 	}
-	if(dregs->cond_reg & DMA_HNDL_ERROR) {
-		printk("esp: DMA shows an error cond_reg<%08lx> addr<%p>\n",
-		       dregs->cond_reg, dregs->st_addr);
-		printk("esp: intr<%2x> stat<%2x> seq<%2x>",
-		       esp->ireg, esp->sreg, esp->seqreg);
-		printk("esp: SCptr->SCp.phase = %d\n", SCptr->SCp.phase);
-		panic("esp: cannot continue\n");
-	}
-	if(esp->sreg & ESP_STAT_PERR) {
-		printk("esp: SCSI bus parity error\n");
-		printk("esp: intr<%2x> stat<%2x> seq<%2x>",
-		       esp->ireg, esp->sreg, esp->seqreg);
-		printk("esp: SCptr->SCp.phase = %d\n", SCptr->SCp.phase);
-		panic("esp: cannot continue\n");
+
+	/* Update the state of our transfer. */
+	SCptr->SCp.ptr += bytes_sent;
+	SCptr->SCp.this_residual -= bytes_sent;
+	if(SCptr->SCp.this_residual < 0) {
+		/* shit */
+		printk("esp%d: Data transfer overrun (%d).\n", esp->esp_id, SCptr->SCp.this_residual);
+		SCptr->SCp.this_residual = 0;
 	}
 
-	/* Service interrupt. */
-	switch(SCptr->SCp.phase) {
-	case not_issued:
-		panic("Unexpected ESP interrupt, current_SC not issued.");
-		break;
-	case in_selection:
-		if(esp->ireg & ESP_INTR_RSEL) {
-			/* XXX Some day XXX */
-			panic("ESP penguin reselected in async mode.");
-		} else if(esp->ireg & ESP_INTR_DC) {
-			/* Either we are scanning the bus and no-one
-			 * lives at this target or it didn't respond.
+	/* Maybe continue. */
+	if(!bogus_data) {
+		ESPDATA(("!bogus_data, "));
+		/* NO MATTER WHAT, we advance the scatterlist,
+		 * if the target should decide to disconnect
+		 * in between scatter chunks (which is common)
+		 * we could die horribly!  I used have the sg
+		 * advance occur only if we are going back into
+		 * (or are staying in) a data phase, you can
+		 * imagine the hell I went through trying to
+		 * figure this out.
+		 */
+		if(SCptr->use_sg && !SCptr->SCp.this_residual)
+			advance_sg(SCptr);
+		if(sreg_datainp(esp->sreg) || sreg_dataoutp(esp->sreg)) {
+			ESPDATA(("to more data\n"));
+			return esp_do_data(esp, eregs);
+		}
+		ESPDATA(("to new phase\n"));
+		return esp_do_phase_determine(esp, eregs);
+	}
+	/* Bogus data, just wait for next interrupt. */
+	ESPLOG(("esp%d: bogus_data during end of data phase\n",
+		esp->esp_id));
+	return do_intr_end;
+}
+
+/* Either a command is completing or a target is dropping off the bus
+ * to continue the command in the background so we can do other work.
+ */
+static inline int esp_do_freebus(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int rval;
+
+	rval = skipahead2(esp, eregs, SCptr, in_status, in_msgindone, in_freeing);
+	if(rval)
+		return rval;
+
+	if(esp->ireg != ESP_INTR_DC) {
+		ESPLOG(("esp%d: Target will not disconnect\n", esp->esp_id));
+		return do_reset_bus; /* target will not drop BSY... */
+	}
+	esp->msgout_len = 0;
+	esp->prevmsgout = NOP;
+	if(esp->prevmsgin == COMMAND_COMPLETE) {
+		/* Normal end of nexus. */
+		if(esp->disconnected_SC)
+			esp_cmd(esp, eregs, ESP_CMD_ESEL);
+
+		if(SCptr->SCp.Status != GOOD &&
+		   ((1<<SCptr->target) & esp->targets_present) &&
+		   SCptr->device->sync && SCptr->device->sync_max_offset) {
+			/* SCSI standard says that the synchronous capabilities
+			 * should be renegotiated at this point.  Most likely
+			 * we are about to request sense from this target
+			 * in which case we want to avoid using sync
+			 * transfers until we are sure of the current target
+			 * state.
 			 */
-			ETRACE(("DISCONNECT\n"));
-#ifdef THREADED_ESP_DRIVER
-			append_SC(esp->eatme_SC, esp->current_SC);
-			esp->current_SC = 0;
-			wake_up(esp_kernel_thread);
-#else
-			esp_done(esp, (DID_NO_CONNECT << 16));
-#endif
-			goto esp_handle_done;
-		} else if((esp->ireg & (ESP_INTR_FDONE | ESP_INTR_BSERV)) ==
-			  (ESP_INTR_FDONE | ESP_INTR_BSERV)) {
-			/* Selection successful, check the sequence step. */
-			/* XXX I know, I know... add error recovery.  XXX */
-			switch(esp->seqreg & ESP_STEP_VBITS) {
-			case ESP_STEP_NCMD:
-				panic("esp: penguin didn't enter cmd phase.");
-				break;
-			case ESP_STEP_PPC:
-				panic("esp: penguin prematurely changed from cmd phase.");
-				break;
-			case ESP_STEP_FINI:
-				/* At the completion of every command
-				 * or message-out phase, we _must_
-				 * unlatch the fifo-flags register
-				 * with an ESP nop command.
-				 */
-				eregs->esp_cmd = ESP_CMD_NULL;
+			ESPMISC(("esp: Status <%d> for target %d lun %d\n",
+				 SCptr->SCp.Status, SCptr->target, SCptr->lun));
+
+			/* But don't do this when spinning up a disk at
+			 * boot time while we poll for completion as it
+			 * fills up the console with messages.  Also, tapes
+			 * can report not ready many times right after
+			 * loading up a tape.
+			 */
+			if(SCptr->cmnd[0] != START_STOP &&
+			   SCptr->data_cmnd[0] != START_STOP &&
+			   SCptr->cmnd[0] != TEST_UNIT_READY &&
+			   SCptr->data_cmnd[0] != TEST_UNIT_READY &&
+			   !(SCptr->device->type == TYPE_TAPE &&
+			     (SCptr->cmnd[0] == TEST_UNIT_READY ||
+			      SCptr->data_cmnd[0] == TEST_UNIT_READY ||
+			      SCptr->cmnd[0] == MODE_SENSE ||
+			      SCptr->data_cmnd[0] == MODE_SENSE)))
+				SCptr->device->sync = 0;
+		}
+		ESPDISC(("F<%02x,%02x>", SCptr->target, SCptr->lun));
+		safe_esp_done(esp, ((SCptr->SCp.Status & 0xff) |
+				    ((SCptr->SCp.Message & 0xff)<<8) |
+				    (DID_OK << 16)));
+	} else if(esp->prevmsgin == DISCONNECT) {
+		/* Normal disconnect. */
+		esp_cmd(esp, eregs, ESP_CMD_ESEL);
+		ESPDISC(("D<%02x,%02x>", SCptr->target, SCptr->lun));
+		append_SC(&esp->disconnected_SC, SCptr);
+		esp->current_SC = NULL;
+		if(esp->issue_SC)
+			esp_exec_cmd(esp);
+	} else {
+		/* Driver bug, we do not expect a disconnect here
+		 * and should not have advanced the state engine
+		 * to in_freeing.
+		 */
+		ESPLOG(("esp%d: last msg not disc and not cmd cmplt.\n",
+			esp->esp_id));
+		return do_reset_bus;
+	}
+	return do_intr_end;
+}
+
+/* Do the needy when a target tries to reconnect to us. */
+static inline int esp_do_reconnect(struct Sparc_ESP *esp, 
+				   struct ESP_regs *eregs)
+{
+	int lun, target;
+	Scsi_Cmnd *SCptr;
+
+	/* Check for all bogus conditions first. */
+	target = reconnect_target(esp, eregs);
+	if(target < 0) {
+		ESPDISC(("bad bus bits\n"));
+		return do_reset_bus;
+	}
+	lun = reconnect_lun(esp, eregs);
+	if(lun < 0) {
+		ESPDISC(("target=%2x, bad identify msg\n", target));
+		return do_reset_bus;
+	}
 
-				/* Selection/Command sequence completed.  We
-				 * (at least for this driver) will be in
-				 * either one of the data phases or status
-				 * phase, check the status register to find
-				 * out.
+	/* Things look ok... */
+	ESPDISC(("R<%02x,%02x>", target, lun));
+	esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+	if(esp100_reconnect_hwbug(esp, eregs))
+		return do_reset_bus;
+	esp_cmd(esp, eregs, ESP_CMD_NULL);
+	SCptr = remove_SC(&esp->disconnected_SC, (unchar) target, (unchar) lun);
+	if(!SCptr) {
+		Scsi_Cmnd *sp;
+
+		ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n",
+			esp->esp_id));
+		ESPLOG(("QUEUE DUMP\n"));
+		sp = esp->issue_SC;
+		ESPLOG(("esp%d: issue_SC[", esp->esp_id));
+		while(sp) {
+			ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
+			sp = (Scsi_Cmnd *) sp->host_scribble;
+		}
+		ESPLOG(("]\n"));
+		sp = esp->current_SC;
+		ESPLOG(("esp%d: current_SC[", esp->esp_id));
+		while(sp) {
+			ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
+			sp = (Scsi_Cmnd *) sp->host_scribble;
+		}
+		ESPLOG(("]\n"));
+		sp = esp->disconnected_SC;
+		ESPLOG(("esp%d: disconnected_SC[", esp->esp_id));
+		while(sp) {
+			ESPLOG(("<%02x,%02x>", sp->target, sp->lun));
+			sp = (Scsi_Cmnd *) sp->host_scribble;
+		}
+		ESPLOG(("]\n"));
+		return do_reset_bus;
+	}
+	esp_connect(esp, eregs, SCptr);
+	esp_cmd(esp, eregs, ESP_CMD_MOK);
+
+	/* No need for explicit restore pointers operation. */
+	esp->snip = 0;
+	esp_advance_phase(SCptr, in_the_dark);
+	return do_intr_end;
+}
+
+/* End of NEXUS (hopefully), pick up status + message byte then leave if
+ * all goes well.
+ */
+static int esp_do_status(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int intr;
+	int rval;
+
+	rval = skipahead1(esp, eregs, SCptr, in_the_dark, in_status);
+	if(rval)
+		return rval;
+
+	intr = esp->ireg;
+	ESPSTAT(("esp_do_status: "));
+	if(intr != ESP_INTR_DC) {
+		int message_out = 0; /* for parity problems */
+
+		/* Ack the message. */
+		ESPSTAT(("ack msg, "));
+		esp_cmd(esp, eregs, ESP_CMD_MOK);
+
+		if(esp->dma_poll)
+			esp->dma_poll(esp, (unsigned char *) esp->esp_command);
+
+		ESPSTAT(("got something, "));
+		/* ESP chimes in with one of
+		 *
+		 * 1) function done interrupt:
+		 *	both status and message in bytes
+		 *	are available
+		 *
+		 * 2) bus service interrupt:
+		 *	only status byte was acquired
+		 *
+		 * 3) Anything else:
+		 *	can't happen, but we test for it
+		 *	anyways
+		 *
+		 * ALSO: If bad parity was detected on either
+		 *       the status _or_ the message byte then
+		 *       the ESP has asserted ATN on the bus
+		 *       and we must therefore wait for the
+		 *       next phase change.
+		 */
+		if(intr & ESP_INTR_FDONE) {
+			/* We got it all, hallejulia. */
+			ESPSTAT(("got both, "));
+			SCptr->SCp.Status = esp->esp_command[0];
+			SCptr->SCp.Message = esp->esp_command[1];
+			esp->prevmsgin = SCptr->SCp.Message;
+			esp->cur_msgin[0] = SCptr->SCp.Message;
+			if(esp->sreg & ESP_STAT_PERR) {
+				/* There was bad parity for the
+				 * message byte, the status byte
+				 * was ok.
 				 */
-				switch(esp->sreg & ESP_STAT_PMASK) {
-				default:
-					printk("esp: Not datain/dataout/status.\n");
-					panic("esp: penguin phase transition after selection.");
-					break;
-				case ESP_DOP:
-					/* Data out phase. */
-					dregs->cond_reg |= DMA_FIFO_INV;
-					while(dregs->cond_reg & DMA_FIFO_ISDRAIN)
-						barrier();
-					SCptr->SCp.phase = in_dataout;
-#ifdef DEBUG_ESP_SG
-					if(SCptr->use_sg)
-						printk("esp: sg-start <%p,%d>",
-						       SCptr->SCp.ptr,
-						       SCptr->SCp.this_residual);
-#endif
-					eregs->esp_tclow = SCptr->SCp.this_residual;
-					eregs->esp_tcmed = (SCptr->SCp.this_residual>>8);
-					eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_NULL);
-
-					/* This is either the one buffer dvma ptr,
-					 * or the first one in the scatter gather
-					 * list.  Check out esp_queue to see how
-					 * this is set up.
-					 */
-					dregs->st_addr = SCptr->SCp.ptr;
-					dregs->cond_reg &= ~(DMA_ST_WRITE);
-					dregs->cond_reg |= (DMA_ENABLE | DMA_INT_ENAB);
-					eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_TI);
-					ETRACE(("DATA_OUT\n"));
-					goto esp_handle_done;
-				case ESP_DIP:
-					/* Data in phase. */
-					dregs->cond_reg |= DMA_FIFO_INV;
-					while(dregs->cond_reg & DMA_FIFO_ISDRAIN)
-						barrier();
-					SCptr->SCp.phase = in_datain;
-#ifdef DEBUG_ESP_SG
-					if(SCptr->use_sg)
-						printk("esp: sg-start <%p,%d>",
-						       SCptr->SCp.ptr,
-						       SCptr->SCp.this_residual);
-#endif
-					eregs->esp_tclow = SCptr->SCp.this_residual;
-					eregs->esp_tcmed = (SCptr->SCp.this_residual>>8);
-					eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_NULL);
-
-					/* This is either the one buffer dvma ptr,
-					 * or the first one in the scatter gather
-					 * list.  Check out esp_queue to see how
-					 * this is set up.
-					 */
-					dregs->st_addr = SCptr->SCp.ptr;
-					dregs->cond_reg |= (DMA_ENABLE | DMA_ST_WRITE | DMA_INT_ENAB);
-					eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_TI);
-					ETRACE(("DATA_IN\n"));
-					goto esp_handle_done;
-				case ESP_STATP:
-					/* Status phase. */
-					SCptr->SCp.phase = in_status;
-					eregs->esp_cmd = ESP_CMD_ICCSEQ;
-					ETRACE(("STATUS\n"));
-					goto esp_handle_done; /* Wait for message. */
-				};
-			};
-		} else if(esp->ireg & ESP_INTR_FDONE) {
-			/* I'd like to investigate why this happens... */
-			ESPLOG(("esp: This is weird, halfway through "));
-			ESPLOG(("selection, trying to continue anyways.\n"));
-			goto esp_handle_done;
+				message_out = MSG_PARITY_ERROR;
+			}
+		} else if(intr == ESP_INTR_BSERV) {
+			/* Only got status byte. */
+			ESPLOG(("esp%d: got status only, ", esp->esp_id));
+			if(!(esp->sreg & ESP_STAT_PERR)) {
+				SCptr->SCp.Status = esp->esp_command[0];
+				SCptr->SCp.Message = 0xff;
+			} else {
+				/* The status byte had bad parity.
+				 * we leave the scsi_pointer Status
+				 * field alone as we set it to a default
+				 * of CHECK_CONDITION in esp_queue.
+				 */
+				message_out = INITIATOR_ERROR;
+			}
 		} else {
-			panic("esp: Did not get bus service during selection.");
-			goto esp_handle_done;
+			/* This shouldn't happen ever. */
+			ESPSTAT(("got bolixed\n"));
+			esp_advance_phase(SCptr, in_the_dark);
+			return esp_do_phase_determine(esp, eregs);
 		}
-		panic("esp: Mr. Potatoe Head is on the loose!");
 
-	case in_datain:
-		/* Drain the fifo for writes to memory. */
-		switch(esp->dma->revision) {
-		case dvmarev0:
-		case dvmarev1:
-		case dvmarevplus:
-		case dvmarev2:
-		case dvmarev3:
-			/* Force a drain. */
-			dregs->cond_reg |= DMA_FIFO_STDRAIN;
-
-			/* fall through */
-		case dvmaesc1:
-			/* Wait for the fifo to drain completely. */
-			while(dregs->cond_reg & DMA_FIFO_ISDRAIN)
-				barrier();
+		if(!message_out) {
+			ESPSTAT(("status=%2x msg=%2x, ", SCptr->SCp.Status,
+				SCptr->SCp.Message));
+			if(SCptr->SCp.Message == COMMAND_COMPLETE) {
+				ESPSTAT(("and was COMMAND_COMPLETE\n"));
+				esp_advance_phase(SCptr, in_freeing);
+				return esp_do_freebus(esp, eregs);
+			} else {
+				ESPLOG(("esp%d: and _not_ COMMAND_COMPLETE\n",
+					esp->esp_id));
+				esp->msgin_len = esp->msgin_ctr = 1;
+				esp_advance_phase(SCptr, in_msgindone);
+				return esp_do_msgindone(esp, eregs);
+			}
+		} else {
+			/* With luck we'll be able to let the target
+			 * know that bad parity happened, it will know
+			 * which byte caused the problems and send it
+			 * again.  For the case where the status byte
+			 * receives bad parity, I do not believe most
+			 * targets recover very well.  We'll see.
+			 */
+			ESPLOG(("esp%d: bad parity somewhere mout=%2x\n",
+				esp->esp_id, message_out));
+			esp->cur_msgout[0] = message_out;
+			esp->msgout_len = esp->msgout_ctr = 1;
+			esp_advance_phase(SCptr, in_the_dark);
+			return esp_do_phase_determine(esp, eregs);
+		}
+	} else {
+		/* If we disconnect now, all hell breaks loose. */
+		ESPLOG(("esp%d: whoops, disconnect\n", esp->esp_id));
+		esp_advance_phase(SCptr, in_the_dark);
+		return esp_do_phase_determine(esp, eregs);
+	}
+}
+
+/* The target has control of the bus and we have to see where it has
+ * taken us.
+ */
+static int esp_do_phase_determine(struct Sparc_ESP *esp,
+				  struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+
+	ESPPHASE(("esp_do_phase_determine: "));
+	if(!(esp->ireg & ESP_INTR_DC)) {
+		switch(esp->sreg & ESP_STAT_PMASK) {
+		case ESP_DOP:
+		case ESP_DIP:
+			ESPPHASE(("to data phase\n"));
+			return esp_do_data(esp, eregs);
+
+		case ESP_STATP:
+			/* Whee, status phase, finish up the command. */
+			ESPPHASE(("to status phase\n"));
+			esp->esp_command[0] = 0xff;
+			esp->esp_command[1] = 0xff;
+#ifdef __mc68000__
+			/* When initiating a DMA read as will happen below
+			 * there should only be made a cache invalidate.
+			 * Therefore this flush. (Hint: The DMA will overwrite
+			 * the two bytes in memory).
+			 */
+			CACHEFLUSHLINE(VTOP(esp->esp_command));
+#endif
+			esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+
+			eregs->esp_tclow = 2;
+			eregs->esp_tcmed = 0;
+
+			esp->dma_init_read(esp, (char *) esp->esp_command, 2);
+
+			esp_advance_phase(SCptr, in_status);
+			esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_ICCSEQ);
+			return esp_do_status(esp, eregs);
+
+		case ESP_MOP:
+			ESPPHASE(("to msgout phase\n"));
+			esp_advance_phase(SCptr, in_msgout);
+			return esp_do_msgout(esp, eregs);
+
+		case ESP_MIP:
+			ESPPHASE(("to msgin phase\n"));
+			esp_advance_phase(SCptr, in_msgin);
+			return esp_do_msgin(esp, eregs);
+
+		case ESP_CMDP:
+			/* Ugh, we're running a non-standard command the
+			 * ESP doesn't understand, one byte at a time.
+			 */
+			ESPPHASE(("to cmd phase\n"));
+			esp_advance_phase(SCptr, in_cmdbegin);
+			return esp_do_cmdbegin(esp, eregs);
+		};
+	} else {
+		Scsi_Device *dp = SCptr->device;
+
+		/* This means real problems if we see this
+		 * here.  Unless we were actually trying
+		 * to force the device to abort/reset.
+		 */
+		ESPLOG(("esp%d Disconnect amidst phases, ", esp->esp_id));
+		ESPLOG(("pphase<%s> cphase<%s>, ",
+			phase_string(SCptr->SCp.phase),
+			phase_string(SCptr->SCp.sent_command)));
+		if(esp->disconnected_SC)
+			esp_cmd(esp, eregs, ESP_CMD_ESEL);
+
+		switch(esp->cur_msgout[0]) {
+		default:
+			/* We didn't expect this to happen at all. */
+			ESPLOG(("device is bolixed\n"));
+			esp_advance_phase(SCptr, in_tgterror);
+			safe_esp_done(esp, (DID_ERROR << 16));
+			break;
+
+		case BUS_DEVICE_RESET:
+			ESPLOG(("device reset successful\n"));
+			dp->sync_max_offset = 0;
+			dp->sync_min_period = 0;
+			dp->sync = 0;
+			esp_advance_phase(SCptr, in_resetdev);
+			safe_esp_done(esp, (DID_RESET << 16));
 			break;
+
+		case ABORT:
+			ESPLOG(("device abort successful\n"));
+			esp_advance_phase(SCptr, in_abortone);
+			safe_esp_done(esp, (DID_ABORT << 16));
+			break;
+
 		};
+		return do_intr_end;
+	}
 
-	case in_dataout:
-		dregs->cond_reg &= ~DMA_ENABLE;
+	ESPLOG(("esp%d: to unknown phase\n", esp->esp_id));
+	printk("esp%d: Bizarre bus phase %2x.\n", esp->esp_id,
+	       esp->sreg & ESP_STAT_PMASK);
+	return do_reset_bus;
+}
+
+/* First interrupt after exec'ing a cmd comes here. */
+static int esp_select_complete(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	Scsi_Device *SDptr = SCptr->device;
+	int cmd_bytes_sent, fcnt;
+
+	esp->seqreg = (eregs->esp_sstep & ESP_STEP_VBITS);
+	fcnt = (eregs->esp_fflags & ESP_FF_FBYTES);
+	cmd_bytes_sent = esp->dma_bytes_sent(esp, fcnt);
+	if(esp->dma_invalidate)
+		esp->dma_invalidate(esp);
+
+	/* Let's check to see if a reselect happened
+	 * while we we're trying to select.  This must
+	 * be checked first.
+	 */
+	if(esp->ireg == (ESP_INTR_RSEL | ESP_INTR_FDONE)) {
+		esp_reconnect(esp, SCptr);
+		return esp_do_reconnect(esp, eregs);
+	}
 
-		/* We may be pipelining an sg-list. */
-		if(SCptr->use_sg) {
-			if(SCptr->SCp.buffers_residual) {
-				/* If we do not see a BUS SERVICE interrupt
-				 * at this point, or we see that we have left
-				 * the current data phase, then we lose.
+	/* Looks like things worked, we should see a bus service &
+	 * a function complete interrupt at this point.  Note we
+	 * are doing a direct comparison because we don't want to
+	 * be fooled into thinking selection was successful if
+	 * ESP_INTR_DC is set, see below.
+	 */
+	if(esp->ireg == (ESP_INTR_FDONE | ESP_INTR_BSERV)) {
+		/* target speaks... */
+		esp->targets_present |= (1<<SCptr->target);
+
+		/* What if the target ignores the sdtr? */
+		if(esp->snip)
+			SDptr->sync = 1;
+
+		/* See how far, if at all, we got in getting
+		 * the information out to the target.
+		 */
+		switch(esp->seqreg) {
+		default:
+
+		case ESP_STEP_ASEL:
+			/* Arbitration won, target selected, but
+			 * we are in some phase which is not command
+			 * phase nor is it message out phase.
+			 *
+			 * XXX We've confused the target, obviously.
+			 * XXX So clear it's state, but we also end
+			 * XXX up clearing everyone elses.  That isn't
+			 * XXX so nice.  I'd like to just reset this
+			 * XXX target, but if I cannot even get it's
+			 * XXX attention and finish selection to talk
+			 * XXX to it, there is not much more I can do.
+			 * XXX If we have a loaded bus we're going to
+			 * XXX spend the next second or so renegotiating
+			 * XXX for synchronous transfers.
+			 */
+			ESPLOG(("esp%d: STEP_ASEL for tgt %d\n",
+				esp->esp_id, SCptr->target));
+
+		case ESP_STEP_SID:
+			/* Arbitration won, target selected, went
+			 * to message out phase, sent one message
+			 * byte, then we stopped.  ATN is asserted
+			 * on the SCSI bus and the target is still
+			 * there hanging on.  This is a legal
+			 * sequence step if we gave the ESP a select
+			 * and stop command.
+			 *
+			 * XXX See above, I could set the borken flag
+			 * XXX in the device struct and retry the
+			 * XXX command.  But would that help for
+			 * XXX tagged capable targets?
+			 */
+
+		case ESP_STEP_NCMD:
+			/* Arbitration won, target selected, maybe
+			 * sent the one message byte in message out
+			 * phase, but we did not go to command phase
+			 * in the end.  Actually, we could have sent
+			 * only some of the message bytes if we tried
+			 * to send out the entire identify and tag
+			 * message using ESP_CMD_SA3.
+			 */
+			cmd_bytes_sent = 0;
+			break;
+
+		case ESP_STEP_PPC:
+			/* No, not the powerPC pinhead.  Arbitration
+			 * won, all message bytes sent if we went to
+			 * message out phase, went to command phase
+			 * but only part of the command was sent.
+			 *
+			 * XXX I've seen this, but usually in conjunction
+			 * XXX with a gross error which appears to have
+			 * XXX occurred between the time I told the
+			 * XXX ESP to arbitrate and when I got the
+			 * XXX interrupt.  Could I have misloaded the
+			 * XXX command bytes into the fifo?  Actually,
+			 * XXX I most likely missed a phase, and therefore
+			 * XXX went into never never land and didn't even
+			 * XXX know it.  That was the old driver though.
+			 * XXX What is even more peculiar is that the ESP
+			 * XXX showed the proper function complete and
+			 * XXX bus service bits in the interrupt register.
+			 */
+
+		case ESP_STEP_FINI4:
+		case ESP_STEP_FINI5:
+		case ESP_STEP_FINI6:
+		case ESP_STEP_FINI7:
+			/* Account for the identify message */
+			if(SCptr->SCp.phase == in_slct_norm)
+				cmd_bytes_sent -= 1;
+		};
+		esp_cmd(esp, eregs, ESP_CMD_NULL);
+
+		/* Be careful, we could really get fucked during synchronous
+		 * data transfers if we try to flush the fifo now.
+		 */
+		if(!fcnt && /* Fifo is empty and... */
+		   /* either we are not doing synchronous transfers or... */
+		   (!SDptr->sync_max_offset ||
+		    /* We are not going into data in phase. */
+		    ((esp->sreg & ESP_STAT_PMASK) != ESP_DIP)))
+			esp_cmd(esp, eregs, ESP_CMD_FLUSH); /* flush is safe */
+
+		/* See how far we got if this is not a slow command. */
+		if(!esp->esp_slowcmd) {
+			if(cmd_bytes_sent < 0)
+				cmd_bytes_sent = 0;
+			if(cmd_bytes_sent != SCptr->cmd_len) {
+				/* Crapola, mark it as a slowcmd
+				 * so that we have some chance of
+				 * keeping the command alive with
+				 * good luck.
+				 *
+				 * XXX Actually, if we didn't send it all
+				 * XXX this means either we didn't set things
+				 * XXX up properly (driver bug) or the target
+				 * XXX or the ESP detected parity on one of
+				 * XXX the command bytes.  This makes much
+				 * XXX more sense, and therefore this code
+				 * XXX should be changed to send out a
+				 * XXX parity error message or if the status
+				 * XXX register shows no parity error then
+				 * XXX just expect the target to bring the
+				 * XXX bus into message in phase so that it
+				 * XXX can send us the parity error message.
+				 * XXX SCSI sucks...
 				 */
-				if(!(esp->ireg & ESP_INTR_BSERV) ||
-				   ((esp->sreg & ESP_STAT_PMASK) > 1))
-					panic("esp: Aiee penguin on the SCSI-bus.");
-
-				++SCptr->SCp.buffer;
-				--SCptr->SCp.buffers_residual;
-				SCptr->SCp.this_residual = SCptr->SCp.buffer->length;
-				SCptr->SCp.ptr = SCptr->SCp.buffer->alt_address;
+				esp->esp_slowcmd = 1;
+				esp->esp_scmdp = &(SCptr->cmnd[cmd_bytes_sent]);
+				esp->esp_scmdleft = (SCptr->cmd_len - cmd_bytes_sent);
+			}
+		}
 
-#ifdef DEBUG_ESP_SG
-				printk("<%p,%d> ", SCptr->SCp.ptr,
-				       SCptr->SCp.this_residual);
-#endif
+		/* Now figure out where we went. */
+		esp_advance_phase(SCptr, in_the_dark);
+		return esp_do_phase_determine(esp, eregs);
+	}
 
-				/* Latch in new esp counters... */
-				eregs->esp_tclow = SCptr->SCp.this_residual;
-				eregs->esp_tcmed = (SCptr->SCp.this_residual>>8);
-				eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_NULL);
+	/* Did the target even make it? */
+	if(esp->ireg == ESP_INTR_DC) {
+		/* wheee... nobody there or they didn't like
+		 * what we told it to do, clean up.
+		 */
+
+		/* If anyone is off the bus, but working on
+		 * a command in the background for us, tell
+		 * the ESP to listen for them.
+		 */
+		if(esp->disconnected_SC)
+			esp_cmd(esp, eregs, ESP_CMD_ESEL);
+
+		if(((1<<SCptr->target) & esp->targets_present) &&
+		   esp->seqreg && esp->cur_msgout[0] == EXTENDED_MESSAGE &&
+		   (SCptr->SCp.phase == in_slct_msg ||
+		    SCptr->SCp.phase == in_slct_stop)) {
+			/* shit */
+			esp->snip = 0;
+			printk("esp%d: Failed synchronous negotiation for target %d "
+			       "lun %d\n",
+			       esp->esp_id, SCptr->target, SCptr->lun);
+			SDptr->sync_max_offset = 0;
+			SDptr->sync_min_period = 0;
+			SDptr->sync = 1; /* so we don't negotiate again */
+
+			/* Run the command again, this time though we
+			 * won't try to negotiate for synchronous transfers.
+			 *
+			 * XXX I'd like to do something like send an
+			 * XXX INITIATOR_ERROR or ABORT message to the
+			 * XXX target to tell it, "Sorry I confused you,
+			 * XXX please come back and I will be nicer next
+			 * XXX time".  But that requires having the target
+			 * XXX on the bus, and it has dropped BSY on us.
+			 */
+			esp->current_SC = NULL;
+			esp_advance_phase(SCptr, not_issued);
+			prepend_SC(&esp->issue_SC, SCptr);
+			esp_exec_cmd(esp);
+			return do_intr_end;
+		}
 
-				/* Reload DVMA gate array with new vaddr and enab. */
-				dregs->st_addr = SCptr->SCp.ptr;
-				dregs->cond_reg |= DMA_ENABLE;
+		/* Ok, this is normal, this is what we see during boot
+		 * or whenever when we are scanning the bus for targets.
+		 * But first make sure that is really what is happening.
+		 */
+		if(((1<<SCptr->target) & esp->targets_present)) {
+			printk("esp%d: Warning, live target %d not responding to "
+			       "selection.\n", esp->esp_id, SCptr->target);
+
+			/* This _CAN_ happen.  The SCSI standard states that
+			 * the target is to _not_ respond to selection if
+			 * _it_ detects bad parity on the bus for any reason.
+			 * Therefore, we assume that if we've talked successfully
+			 * to this target before, bad parity is the problem.
+			 */
+			safe_esp_done(esp, (DID_PARITY << 16));
+		} else {
+			/* Else, there really isn't anyone there. */
+			ESPMISC(("esp: selection failure, maybe nobody there?\n"));
+			ESPMISC(("esp: target %d lun %d\n",
+				 SCptr->target, SCptr->lun));
+			safe_esp_done(esp, (DID_BAD_TARGET << 16));
+		}
+		return do_intr_end;
+	}
 
-				/* Tell the esp to start transferring. */
-				eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_TI);
-				goto esp_handle_done;
+
+	ESPLOG(("esp%d: Selection failure.\n", esp->esp_id));
+	printk("esp%d: Currently -- ", esp->esp_id);
+	esp_print_ireg(esp->ireg);
+	printk(" ");
+	esp_print_statreg(esp->sreg);
+	printk(" ");
+	esp_print_seqreg(esp->seqreg);
+	printk("\n");
+	printk("esp%d: New -- ", esp->esp_id);
+	esp->sreg = eregs->esp_status;
+	esp->seqreg = eregs->esp_sstep;
+	esp->ireg = eregs->esp_intrpt;
+
+	esp_print_ireg(esp->ireg);
+	printk(" ");
+	esp_print_statreg(esp->sreg);
+	printk(" ");
+	esp_print_seqreg(esp->seqreg);
+	printk("\n");
+	ESPLOG(("esp%d: resetting bus\n", esp->esp_id));
+	return do_reset_bus; /* ugh... */
+}
+
+/* Continue reading bytes for msgin phase. */
+static int esp_do_msgincont(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	if(esp->ireg & ESP_INTR_BSERV) {
+		/* in the right phase too? */
+		if((esp->sreg & ESP_STAT_PMASK) == ESP_MIP) {
+			/* phew... */
+			esp_cmd(esp, eregs, ESP_CMD_TI);
+			esp_advance_phase(esp->current_SC, in_msgindone);
+			return do_intr_end;
+		}
+
+		/* We changed phase but ESP shows bus service,
+		 * in this case it is most likely that we, the
+		 * hacker who has been up for 20hrs straight
+		 * staring at the screen, drowned in coffee
+		 * smelling like retched cigarette ashes
+		 * have miscoded something..... so, try to
+		 * recover as best we can.
+		 */
+		printk("esp%d: message in mis-carriage.\n", esp->esp_id);
+	}
+	esp_advance_phase(esp->current_SC, in_the_dark);
+	return do_phase_determine;
+}
+
+static inline int check_singlebyte_msg(struct Sparc_ESP *esp,
+				       struct ESP_regs *eregs)
+{
+	esp->prevmsgin = esp->cur_msgin[0];
+	if(esp->cur_msgin[0] & 0x80) {
+		/* wheee... */
+		ESPLOG(("esp%d: target sends identify amidst phases\n",
+			esp->esp_id));
+		esp_advance_phase(esp->current_SC, in_the_dark);
+		return 0;
+	} else if(((esp->cur_msgin[0] & 0xf0) == 0x20) ||
+		  (esp->cur_msgin[0] == EXTENDED_MESSAGE)) {
+		esp->msgin_len = 2;
+		esp_advance_phase(esp->current_SC, in_msgincont);
+		return 0;
+	}
+	esp_advance_phase(esp->current_SC, in_the_dark);
+	switch(esp->cur_msgin[0]) {
+	default:
+		/* We don't want to hear about it. */
+		ESPLOG(("esp%d: msg %02x which we dont know about\n", esp->esp_id,
+			esp->cur_msgin[0]));
+		return MESSAGE_REJECT;
+
+	case NOP:
+		ESPLOG(("esp%d: target %d sends a nop\n", esp->esp_id,
+			esp->current_SC->target));
+		return 0;
+
+	case RESTORE_POINTERS:
+	case SAVE_POINTERS:
+		/* We handle this all automatically. */
+		return 0;
+
+	case COMMAND_COMPLETE:
+	case DISCONNECT:
+		/* Freeing the bus, let it go. */
+		esp->current_SC->SCp.phase = in_freeing;
+		return 0;
+
+	case MESSAGE_REJECT:
+		ESPMISC(("msg reject, "));
+		if(esp->prevmsgout == EXTENDED_MESSAGE) {
+			Scsi_Device *SDptr = esp->current_SC->device;
+
+			/* Doesn't look like this target can
+			 * do synchronous transfers.
+			 */
+			ESPSDTR(("got reject, was trying sync nego, clearing sync\n"));
+			SDptr->sync = 1;
+			SDptr->sync_min_period = 0;
+			SDptr->sync_max_offset = 0;
+			return 0;
+		} else {
+			ESPMISC(("not sync nego, sending ABORT\n"));
+			return ABORT;
+		}
+	};
+}
+
+/* Target negotiates for synchronous transfers before we do, this
+ * is legal although very strange.  What is even funnier is that
+ * the SCSI2 standard specifically recommends against targets doing
+ * this because so many initiators cannot cope with this occuring.
+ */
+static inline int target_with_ants_in_pants(struct Sparc_ESP *esp,
+					    Scsi_Cmnd *SCptr,
+					    Scsi_Device *SDptr)
+{
+	if(SDptr->sync || SDptr->borken) {
+		/* sorry, no can do */
+		ESPSDTR(("forcing to async, "));
+		build_sync_nego_msg(esp, 0, 0);
+		SDptr->sync = 1;
+		esp->snip = 1;
+		ESPLOG(("esp%d: hoping for msgout\n", esp->esp_id));
+		esp_advance_phase(SCptr, in_the_dark);
+		return EXTENDED_MESSAGE;
+	}
+
+	/* Ok, we'll check them out... */
+	return 0;
+}
+
+static inline void sync_report(struct Sparc_ESP *esp)
+{
+	int msg3, msg4;
+
+	msg3 = esp->cur_msgin[3];
+	msg4 = esp->cur_msgin[4];
+	if(msg4) {
+		int hz = 1000000000 / (msg3 * 4);
+		int integer = hz / 1000000;
+		int fraction = (hz - (integer * 1000000)) / 10000;
+		printk("esp%d: target %d [period %dns offset %d %d.%02dMHz %s SCSI%s]\n",
+		       esp->esp_id, esp->current_SC->target,
+		       (int) msg3 * 4,
+		       (int) msg4,
+		       integer, fraction,
+		       (((msg3 * 4) < 200) ? "FAST" : "synchronous"),
+		       (((msg3 * 4) < 200) ? "-II" : ""));
+	} else {
+		printk("esp%d: target %d asynchronous\n",
+		       esp->esp_id, esp->current_SC->target);
+	}
+}
+
+static inline int check_multibyte_msg(struct Sparc_ESP *esp,
+				       struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	Scsi_Device *SDptr = SCptr->device;
+	unchar regval = 0;
+	int message_out = 0;
+
+	ESPSDTR(("chk multibyte msg: "));
+	if(esp->cur_msgin[2] == EXTENDED_SDTR) {
+		int period = esp->cur_msgin[3];
+		int offset = esp->cur_msgin[4];
+
+		ESPSDTR(("is sync nego response, "));
+		if(!esp->snip) {
+			int rval;
+
+			/* Target negotiates first! */
+			ESPSDTR(("target jumps the gun, "));
+			message_out = EXTENDED_MESSAGE; /* we must respond */
+			rval = target_with_ants_in_pants(esp, SCptr, SDptr);
+			if(rval)
+				return rval;
+		}
+
+		ESPSDTR(("examining sdtr, "));
+
+		/* Offset cannot be larger than ESP fifo size. */
+		if(offset > 15) {
+			ESPSDTR(("offset too big %2x, ", offset));
+			offset = 15;
+			ESPSDTR(("sending back new offset\n"));
+			build_sync_nego_msg(esp, period, offset);
+			return EXTENDED_MESSAGE;
+		}
+
+		if(offset && period > esp->max_period) {
+			/* Yeee, async for this slow device. */
+			ESPSDTR(("period too long %2x, ", period));
+			build_sync_nego_msg(esp, 0, 0);
+			ESPSDTR(("hoping for msgout\n"));
+			esp_advance_phase(esp->current_SC, in_the_dark);
+			return EXTENDED_MESSAGE;
+		} else if (offset && period < esp->min_period) {
+			ESPSDTR(("period too short %2x, ", period));
+			period = esp->min_period;
+			if(esp->erev > esp236)
+				regval = 4;
+			else
+				regval = 5;
+		} else if(offset) {
+			int tmp;
+
+			ESPSDTR(("period is ok, "));
+			tmp = esp->ccycle / 1000;
+			regval = (((period << 2) + tmp - 1) / tmp);
+			if(regval && ((esp->erev == fas100a ||
+				       esp->erev == fas236))) {
+				if(period >= 50)
+					regval--;
 			}
-#ifdef DEBUG_ESP_SG
-			printk("done.\n");
+		}
+
+		if(offset) {
+
+			unchar bit;
+
+			SDptr->sync_min_period = (regval & 0x1f);
+			SDptr->sync_max_offset = (offset | esp->radelay);
+			if((esp->erev == fas100a || esp->erev == fas236)) {
+				if(esp->erev == fas100a)
+					bit = ESP_CONFIG3_FAST;
+				else
+					bit = ESP_CONFIG3_FSCSI;
+				if(period < 50)
+					esp->config3[SCptr->target] |= bit;
+				else
+					esp->config3[SCptr->target] &= ~bit;
+				eregs->esp_cfg3 = esp->config3[SCptr->target];
+			}
+			eregs->esp_soff = SDptr->sync_min_period;
+			eregs->esp_stp  = SDptr->sync_max_offset;
+
+			ESPSDTR(("soff=%2x stp=%2x cfg3=%2x\n",
+				SDptr->sync_max_offset,
+				SDptr->sync_min_period,
+				esp->config3[SCptr->target]));
+
+			esp->snip = 0;
+		} else if(SDptr->sync_max_offset) {
+			unchar bit;
+
+			/* back to async mode */
+			ESPSDTR(("unaccaptable sync nego, forcing async\n"));
+			SDptr->sync_max_offset = 0;
+			SDptr->sync_min_period = 0;
+			eregs->esp_soff = 0;
+			eregs->esp_stp = 0;
+			if((esp->erev == fas100a || esp->erev == fas236)) {
+				if(esp->erev == fas100a)
+					bit = ESP_CONFIG3_FAST;
+				else
+					bit = ESP_CONFIG3_FSCSI;
+				esp->config3[SCptr->target] &= ~bit;
+				eregs->esp_cfg3 = esp->config3[SCptr->target];
+			}
+		}
+
+		sync_report(esp);
+
+		ESPSDTR(("chk multibyte msg: sync is known, "));
+		SDptr->sync = 1;
+
+		if(message_out) {
+			ESPLOG(("esp%d: sending sdtr back, hoping for msgout\n",
+				esp->esp_id));
+			build_sync_nego_msg(esp, period, offset);
+			esp_advance_phase(SCptr, in_the_dark);
+			return EXTENDED_MESSAGE;
+		}
+
+		ESPSDTR(("returning zero\n"));
+		esp_advance_phase(SCptr, in_the_dark); /* ...or else! */
+		return 0;
+	} else if(esp->cur_msgin[2] == EXTENDED_MODIFY_DATA_POINTER) {
+		ESPLOG(("esp%d: rejecting modify data ptr msg\n", esp->esp_id));
+		message_out = MESSAGE_REJECT;
+	}
+	esp_advance_phase(SCptr, in_the_dark);
+	return message_out;
+}
+
+static int esp_do_msgindone(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+	int message_out = 0, it = 0;
+	int rval;
+
+	rval = skipahead1(esp, eregs, SCptr, in_msgin, in_msgindone);
+	if(rval)
+		return rval;
+
+	if(SCptr->SCp.sent_command != in_status) {
+		if(!(esp->ireg & ESP_INTR_DC)) {
+			if(esp->msgin_len && (esp->sreg & ESP_STAT_PERR)) {
+				message_out = MSG_PARITY_ERROR;
+				esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+			} else if((it = (eregs->esp_fflags & ESP_FF_FBYTES))!=1) {
+				/* We certainly dropped the ball somewhere. */
+				message_out = INITIATOR_ERROR;
+				esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+			} else if(!esp->msgin_len) {
+				it = eregs->esp_fdata;
+				esp_advance_phase(SCptr, in_msgincont);
+			} else {
+				/* it is ok and we want it */
+				it = esp->cur_msgin[esp->msgin_ctr] = eregs->esp_fdata;
+				esp->msgin_ctr++;
+			}
+		} else {
+			esp_advance_phase(SCptr, in_the_dark);
+			return do_work_bus;
+		}
+	} else {
+		it = esp->cur_msgin[0];
+	}
+	if(!message_out && esp->msgin_len) {
+		if(esp->msgin_ctr < esp->msgin_len) {
+			esp_advance_phase(SCptr, in_msgincont);
+		} else if(esp->msgin_len == 1) {
+			message_out = check_singlebyte_msg(esp, eregs);
+		} else if(esp->msgin_len == 2) {
+			if(esp->cur_msgin[0] == EXTENDED_MESSAGE) {
+				if((it+2) >= 15) {
+					message_out = MESSAGE_REJECT;
+				} else {
+					esp->msgin_len = (it + 2);
+					esp_advance_phase(SCptr, in_msgincont);
+				}
+			} else {
+				message_out = MESSAGE_REJECT; /* foo on you */
+			}
+		} else {
+			message_out = check_multibyte_msg(esp, eregs);
+		}
+	}
+	if(message_out < 0) {
+		return -message_out;
+	} else if(message_out) {
+		if(((message_out != 1) &&
+		    ((message_out < 0x20) || (message_out & 0x80))))
+			esp->msgout_len = 1;
+		esp->cur_msgout[0] = message_out;
+		esp_cmd(esp, eregs, ESP_CMD_SATN);
+		esp_advance_phase(SCptr, in_msgincont);
+		esp->msgin_len = 0;
+	}
+	esp->sreg = eregs->esp_status;
+	esp->sreg &= ~(ESP_STAT_INTR);
+	if((esp->sreg & (ESP_STAT_PMSG|ESP_STAT_PCD)) == (ESP_STAT_PMSG|ESP_STAT_PCD))
+		esp_cmd(esp, eregs, ESP_CMD_MOK);
+	if((SCptr->SCp.sent_command == in_msgindone) &&
+	    (SCptr->SCp.phase == in_freeing))
+		return esp_do_freebus(esp, eregs);
+	return do_intr_end;
+}
+
+static int esp_do_cmdbegin(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+
+	esp_advance_phase(SCptr, in_cmdend);
+	esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+	eregs->esp_fdata = *esp->esp_scmdp++;
+	esp->esp_scmdleft--;
+	esp_cmd(esp, eregs, ESP_CMD_TI);
+	return do_intr_end;
+}
+
+static inline int esp_do_cmddone(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	esp_cmd(esp, eregs, ESP_CMD_NULL);
+	if(esp->ireg & ESP_INTR_BSERV) {
+		esp_advance_phase(esp->current_SC, in_the_dark);
+		return esp_do_phase_determine(esp, eregs);
+	}
+	ESPLOG(("esp%d: in do_cmddone() but didn't get BSERV interrupt.\n",
+		esp->esp_id));
+	return do_reset_bus;
+}
+
+static int esp_do_msgout(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+	switch(esp->msgout_len) {
+	case 1:
+		eregs->esp_fdata = esp->cur_msgout[0];
+		esp_cmd(esp, eregs, ESP_CMD_TI);
+		break;
+
+	case 2:
+		esp->esp_command[0] = esp->cur_msgout[0];
+		esp->esp_command[1] = esp->cur_msgout[1];
+		esp->dma_setup(esp, (char *) esp->esp_command, 2, 0);
+		esp_setcount(eregs, 2);
+		esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+		break;
+
+	case 5:
+		esp->esp_command[0] = esp->cur_msgout[0];
+		esp->esp_command[1] = esp->cur_msgout[1];
+		esp->esp_command[2] = esp->cur_msgout[2];
+		esp->esp_command[3] = esp->cur_msgout[3];
+		esp->esp_command[4] = esp->cur_msgout[4];
+		esp->esp_command[5] = esp->cur_msgout[5];
+		esp->snip = 1;
+		esp->dma_setup(esp, (char *) esp->esp_command, 5, 0);
+		esp_setcount(eregs, 5);
+		esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
+		break;
+
+	default:
+		/* whoops */
+		ESPMISC(("bogus msgout sending NOP\n"));
+		eregs->esp_fdata = NOP;
+		esp->msgout_len = 1;
+		esp_cmd(esp, eregs, ESP_CMD_TI);
+		break;
+	}
+	esp_advance_phase(esp->current_SC, in_msgoutdone);
+	return do_intr_end;
+}
+
+static inline int esp_do_msgoutdone(struct Sparc_ESP *esp, 
+				    struct ESP_regs *eregs)
+{
+	if((esp->msgout_len > 1) && esp->dma_barrier)
+		esp->dma_barrier(esp);
+
+	if(!(esp->ireg & ESP_INTR_DC)) {
+		esp_cmd(esp, eregs, ESP_CMD_NULL);
+		switch(esp->sreg & ESP_STAT_PMASK) {
+		case ESP_MOP:
+			/* whoops, parity error */
+			ESPLOG(("esp%d: still in msgout, parity error assumed\n",
+				esp->esp_id));
+			if(esp->msgout_len > 1)
+				esp_cmd(esp, eregs, ESP_CMD_SATN);
+			esp_advance_phase(esp->current_SC, in_msgout);
+			return do_work_bus;
+
+		case ESP_DIP:
+			break;
+
+		default:
+			if(!fcount(eregs) &&
+			   !(esp->current_SC->device->sync_max_offset))
+				esp_cmd(esp, eregs, ESP_CMD_FLUSH);
+			break;
+
+		};
+	} else {
+		ESPLOG(("esp%d: disconnect, resetting bus\n", esp->esp_id));
+		return do_reset_bus;
+	}
+
+	/* If we sent out a synchronous negotiation message, update
+	 * our state.
+	 */
+	if(esp->cur_msgout[2] == EXTENDED_MESSAGE &&
+	   esp->cur_msgout[4] == EXTENDED_SDTR) {
+		esp->snip = 1; /* anal retentiveness... */
+	}
+
+	esp->prevmsgout = esp->cur_msgout[0];
+	esp->msgout_len = 0;
+	esp_advance_phase(esp->current_SC, in_the_dark);
+	return esp_do_phase_determine(esp, eregs);
+}
+
+/* This is the second tier in our dual-level SCSI state machine. */
+static inline int esp_work_bus(struct Sparc_ESP *esp, struct ESP_regs *eregs)
+{
+	Scsi_Cmnd *SCptr = esp->current_SC;
+
+	ESPBUS(("esp_work_bus: "));
+	if(!SCptr) {
+		ESPBUS(("reconnect\n"));
+		return esp_do_reconnect(esp, eregs);
+	}
+
+	switch(SCptr->SCp.phase) {
+	case in_the_dark:
+		ESPBUS(("in the dark\n"));
+		return esp_do_phase_determine(esp, eregs);
+
+	case in_slct_norm:
+	case in_slct_stop:
+	case in_slct_msg:
+	case in_slct_tag:
+	case in_slct_sneg:
+		ESPBUS(("finish selection\n"));
+		return esp_select_complete(esp, eregs);
+
+	case in_datain:
+	case in_dataout:
+		ESPBUS(("finish data\n"));
+		return esp_do_data_finale(esp, eregs);
+
+	case in_msgout:
+		ESPBUS(("message out "));
+		return esp_do_msgout(esp, eregs);
+
+	case in_msgoutdone:
+		ESPBUS(("finish message out "));
+		return esp_do_msgoutdone(esp, eregs);
+
+	case in_msgin:
+		ESPBUS(("message in "));
+		return esp_do_msgin(esp, eregs);
+
+	case in_msgincont:
+		ESPBUS(("continue message in "));
+		return esp_do_msgincont(esp, eregs);
+
+	case in_msgindone:
+		ESPBUS(("finish message in "));
+		return esp_do_msgindone(esp, eregs);
+
+	case in_status:
+		ESPBUS(("status phase "));
+		return esp_do_status(esp, eregs);
+
+	case in_freeing:
+		ESPBUS(("freeing the bus "));
+		return esp_do_freebus(esp, eregs);
+
+	case in_cmdbegin:
+		ESPBUS(("begin slow cmd "));
+		return esp_do_cmdbegin(esp, eregs);
+
+	case in_cmdend:
+		ESPBUS(("end slow cmd "));
+		return esp_do_cmddone(esp, eregs);
+
+	default:
+		printk("esp%d: command in weird state %2x\n",
+		       esp->esp_id, esp->current_SC->SCp.phase);
+		return do_reset_bus;
+	};
+}
+
+/* Main interrupt handler for an esp adapter. */
+inline void esp_handle(struct Sparc_ESP *esp)
+{
+	struct ESP_regs *eregs;
+	Scsi_Cmnd *SCptr;
+	int what_next = do_intr_end;
+#ifdef CONFIG_SCSI_SUNESP
+	struct dma_registers *dregs = esp->dregs;
 #endif
+	eregs = esp->eregs;
+	SCptr = esp->current_SC;
+
+	if(esp->dma_irq_entry)
+		esp->dma_irq_entry(esp);
+
+	/* Check for errors. */
+	esp->sreg = eregs->esp_status;
+	esp->sreg &= (~ESP_STAT_INTR);
+
+	if(esp->sreg & (ESP_STAT_SPAM)) {
+		/* Gross error, could be due to one of:
+		 *
+		 * - top of fifo overwritten, could be because
+		 *   we tried to do a synchronous transfer with
+		 *   an offset greater than ESP fifo size
+		 *
+		 * - top of command register overwritten
+		 *
+v		 * - DMA setup to go in one direction, SCSI
+		 *   bus points in the other, whoops
+		 *
+		 * - weird phase change during asynchronous
+		 *   data phase while we are initiator
+		 */
+		ESPLOG(("esp%d: Gross error sreg=%2x\n", esp->esp_id, esp->sreg));
+
+		/* If a command is live on the bus we cannot safely
+		 * reset the bus, so we'll just let the pieces fall
+		 * where they may.  Here we are hoping that the
+		 * target will be able to cleanly go away soon
+		 * so we can safely reset things.
+		 */
+		if(!SCptr) {
+			ESPLOG(("esp%d: No current cmd during gross error, "
+				"resetting bus\n", esp->esp_id));
+			what_next = do_reset_bus;
+			goto again;
 		}
-		/* Take a look at what happened. */
-		if(esp->ireg & ESP_INTR_DC) {
-			panic("esp: target disconnects during data transfer.");
-			goto esp_handle_done;
-		} else if(esp->ireg & ESP_INTR_BSERV) {
-			if((esp->sreg & ESP_STAT_PMASK) != ESP_STATP) {
-				panic("esp: Not status phase after data phase.");
+	}
+
+#ifdef CONFIG_SCSI_SUNESP
+	if(dregs->cond_reg & DMA_HNDL_ERROR) {
+		/* A DMA gate array error.  Here we must
+		 * be seeing one of two things.  Either the
+		 * virtual to physical address translation
+		 * on the SBUS could not occur, else the
+		 * translation it did get pointed to a bogus
+		 * page.  Ho hum...
+		 */
+		ESPLOG(("esp%d: DMA error %08lx\n", esp->esp_id,
+			dregs->cond_reg));
+
+		/* DMA gate array itself must be reset to clear the
+		 * error condition.
+		 */
+		if(esp->dma_reset)
+			esp->dma_reset(esp);
+
+		what_next = do_reset_bus;
+		goto again;
+	}
+#endif /* CONFIG_SCSI_SUNESP */
+
+	esp->ireg = eregs->esp_intrpt;   /* Unlatch intr and stat regs */
+
+	/* No current cmd is only valid this point when there are
+	 * commands off the bus or we are trying a reset.
+	 */
+	if(!SCptr && !esp->disconnected_SC && !(esp->ireg & ESP_INTR_SR)) {
+		/* Panic is safe, since current_SC is null. */
+		ESPLOG(("esp%d: no command in esp_handle()\n", esp->esp_id));
+		panic("esp_handle: current_SC == penguin within interrupt!");
+	}
+
+	if(esp->ireg & (ESP_INTR_IC)) {
+		/* Illegal command fed to ESP.  Outside of obvious
+		 * software bugs that could cause this, there is
+		 * a condition with esp100 where we can confuse the
+		 * ESP into an erroneous illegal command interrupt
+		 * because it does not scrape the FIFO properly
+		 * for reselection.  See esp100_reconnect_hwbug()
+		 * to see how we try very hard to avoid this.
+		 */
+		ESPLOG(("esp%d: illegal command\n", esp->esp_id));
+
+		esp_dump_state(esp, eregs);
+
+		if(SCptr) {
+			/* Devices with very buggy firmware can drop BSY
+			 * during a scatter list interrupt when using sync
+			 * mode transfers.  We continue the transfer as
+			 * expected, the target drops the bus, the ESP
+			 * gets confused, and we get a illegal command
+			 * interrupt because the bus is in the disconnected
+			 * state now and ESP_CMD_TI is only allowed when
+			 * a nexus is alive on the bus.
+			 */
+			ESPLOG(("esp%d: Forcing async and disabling disconnect for "
+				"target %d\n", esp->esp_id, SCptr->target));
+			SCptr->device->borken = 1; /* foo on you */
+		}
+
+		what_next = do_reset_bus;
+		goto again;
+	}
+
+	if(!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) {
+		int phase;
+		if(SCptr) {
+			phase = SCptr->SCp.phase;
+			if(phase & in_phases_mask) {
+				what_next = esp_work_bus(esp, eregs);
+			} else if(phase & in_slct_mask) {
+				what_next = esp_select_complete(esp, eregs);
+			} else {
+				ESPLOG(("esp%d: interrupt for no good reason...\n",
+					esp->esp_id));
 				goto esp_handle_done;
 			}
-			SCptr->SCp.phase = in_status;
-			eregs->esp_cmd = ESP_CMD_ICCSEQ;
-			ETRACE(("STATUS\n"));
-			goto esp_handle_done; /* Wait for message. */
 		} else {
-			printk("esp: did not get bus service after data transfer.");
-			printk("esp_status: intr<%2x> stat<%2x> seq<%2x>\n",
-			       esp->ireg, esp->sreg, esp->seqreg);
-			panic("esp: penguin data transfer.");
-			goto esp_handle_done;
+			ESPLOG(("esp%d: BSERV or FDONE or DC while SCptr==NULL\n",
+				esp->esp_id));
+			what_next = do_reset_bus;
+			goto again;
 		}
-	case in_status:
-		if(esp->ireg & ESP_INTR_DC) {
-			panic("esp: penguin disconnects in status phase.");
-			goto esp_handle_done;
-		} else if (esp->ireg & ESP_INTR_FDONE) {
-			/* Status and Message now sit in the fifo for us. */
-			last_fflags = eregs->esp_fflags;
-			SCptr->SCp.phase   = in_finale;
-			last_status = SCptr->SCp.Status  = eregs->esp_fdata;
-			last_msg = SCptr->SCp.Message = eregs->esp_fdata;
-			eregs->esp_cmd = ESP_CMD_MOK;
-			ETRACE(("FINALE\n"));
-			goto esp_handle_done;
+	} else if(esp->ireg & ESP_INTR_SR) {
+		ESPLOG(("esp%d: SCSI bus reset interrupt\n", esp->esp_id));
+		what_next = do_reset_complete;
+	} else if(esp->ireg & (ESP_INTR_S | ESP_INTR_SATN)) {
+		ESPLOG(("esp%d: AIEEE we have been selected by another initiator!\n",
+			esp->esp_id));
+		what_next = do_reset_bus;
+		goto again;
+	} else if(esp->ireg & ESP_INTR_RSEL) {
+		if(!SCptr) {
+			/* This is ok. */
+			what_next = esp_do_reconnect(esp, eregs);
+		} else if(SCptr->SCp.phase & in_slct_mask) {
+			/* Only selection code knows how to clean
+			 * up properly.
+			 */
+			ESPDISC(("Reselected during selection attempt\n"));
+			what_next = esp_select_complete(esp, eregs);
 		} else {
-			panic("esp: penguin status phase.");
+			ESPLOG(("esp%d: Reselected while bus is busy\n",
+				esp->esp_id));
+			what_next = do_reset_bus;
+			goto again;
 		}
-	case in_finale:
-		if(esp->ireg & ESP_INTR_BSERV) {
-			panic("esp: penguin doesn't disconnect after status msg-ack.");
-			goto esp_handle_done;
-		} else if(esp->ireg & ESP_INTR_DC) {
-			/* Nexus is complete. */
-#ifdef THREADED_ESP_DRIVER
-			append_SC(esp->eatme_SC, esp->current_SC);
-			esp->current_SC = 0;
-			wake_up(esp_kernel_thread);
-#else
-			esp_done(esp, ((SCptr->SCp.Status & 0xff) |
-				 ((SCptr->SCp.Message & 0xff) << 8) |
-				 (DID_OK << 16)));
+	}
+
+	/* We're trying to fight stack problems, and inline as much as
+	 * possible without making this driver a mess. hate hate hate
+	 * This is tier-one in our dual level SCSI state machine.
+	 */
+again:
+	switch(what_next) {
+	case do_intr_end:
+		goto esp_handle_done;
+
+	case do_work_bus:
+		what_next = esp_work_bus(esp, eregs);
+		break;
+
+	case do_phase_determine:
+		what_next = esp_do_phase_determine(esp, eregs);
+		break;
+
+	case do_reset_bus:
+		ESPLOG(("esp%d: resetting bus...\n", esp->esp_id));
+		esp->resetting_bus = 1;
+		esp_cmd(esp, eregs, ESP_CMD_RS);
+		goto esp_handle_done;
+
+	case do_reset_complete:
+		/* Tricky, we don't want to cause any more commands to
+		 * go out until we clear all the live cmds by hand.
+		 */
+#ifdef CONFIG_SCSI_SUNESP
+		if(!DMA_ISBROKEN(esp))
 #endif
-			ETRACE(("NEXUS_COMPLETE\n"));
-			goto esp_handle_done;
-		} else {
-			printk("esp: wacky state while in in_finale phase.\n");
-			printk("esp_status: intr<%2x> stat<%2x> seq<%2x>\n",
-			       esp->ireg, esp->sreg, esp->seqreg);
-			panic("esp: penguin esp state.");
+			esp->dma_ints_off(esp);
+		if(esp->current_SC) {
+			Scsi_Cmnd *SCptr = esp->current_SC;
+
+#ifdef CONFIG_SCSI_SUNESP
+			if(!SCptr->use_sg)
+				mmu_release_scsi_one((char *)
+						     SCptr->SCp.have_data_in,
+						     SCptr->request_bufflen,
+						     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+			else
+				mmu_release_scsi_sgl((struct mmu_sglist *)
+						     SCptr->buffer,
+						     SCptr->use_sg - 1,
+						     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+#endif
+			SCptr->result = (DID_RESET << 16);
+			SCptr->scsi_done(SCptr);
+			cli();
+		}
+		esp->current_SC = NULL;
+		if(esp->disconnected_SC) {
+			Scsi_Cmnd *SCptr;
+			while((SCptr = remove_first_SC(&esp->disconnected_SC))) {
+				if(!SCptr->use_sg)
+#ifdef CONFIG_SCSI_SUNESP
+					mmu_release_scsi_one((char *)
+							     SCptr->SCp.have_data_in,
+							     SCptr->request_bufflen,
+							     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+				else
+					mmu_release_scsi_sgl((struct mmu_sglist *)
+							     SCptr->buffer,
+							     SCptr->use_sg - 1,
+							     ((struct linux_sbus_device *) (esp->edev))->my_bus);
+#endif
+				SCptr->result = (DID_RESET << 16);
+				SCptr->scsi_done(SCptr);
+				cli();
+			}
+		}
+		esp->resetting_bus = 0;
+#ifdef CONFIG_SCSI_SUNESP
+		if(!DMA_ISBROKEN(esp))
+#endif
+			esp->dma_ints_on(esp);
+
+		if(esp->current_SC) {
+			printk("esp%d: weird weird weird, current_SC not NULL after "
+			       "SCSI bus reset.\n", esp->esp_id);
 			goto esp_handle_done;
 		}
-	default:
-		panic("esp: detected penguin phase.");
+
+		/* Now it is safe to execute more things. */
+		if(esp->issue_SC){
+			esp_exec_cmd(esp);
+		}
 		goto esp_handle_done;
-	}
-	panic("esp: Heading to the promised land.");
+
+	default:
+		/* state is completely lost ;-( */
+		ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n",
+			esp->esp_id));
+		what_next = do_reset_bus;
+		break;
+
+	};
+	goto again;
 
 esp_handle_done:
-	DMA_IRQ_EXIT(esp->dma, dregs);
+	if(esp->dma_irq_exit)
+		esp->dma_irq_exit(esp);
 	return;
 }
 
-static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
+void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
 {
 	struct Sparc_ESP *esp;
 
+	static int running = 0;
+	int again;
+
+	/* It is ok to take irq's on one esp while the other
+	 * is amidst the processing of a reset.
+	 */
+	running++;
+	if(running > esps_running)
+		ESPLOG(("esp_intr: yieee, recursive interrupt!\n"));
+
 	/* Handle all ESP interrupts showing */
+repeat:
+	again = 0;
 	for_each_esp(esp) {
-		if(DMA_IRQ_P(esp->dregs)) {
+		if(esp->dma_irq_p(esp)) {
+			again = 1;
+			ESPIRQ(("I%d(", esp->esp_id));
 			esp_handle(esp);
+			ESPIRQ((")"));
 		}
 	}
+	if(again)
+		goto repeat;
+
+	running--;
 }
diff -urN pc-2.0.21/drivers/scsi/esp.h linux-2.0.21/drivers/scsi/esp.h
--- pc-2.0.21/drivers/scsi/esp.h	Tue Apr 23 11:31:34 1996
+++ linux-2.0.21/drivers/scsi/esp.h	Sun Sep  8 16:25:21 1996
@@ -4,11 +4,97 @@
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
  */
 
-#ifndef _SPARC_ESP_H
-#define _SPARC_ESP_H
+#ifndef _ESP_H
+#define _ESP_H
 
-/* For dvma controller register definitions. */
-#include <asm/dma.h>
+/* Macros for debugging messages */
+
+/* #define DEBUG_ESP */
+/* #define DEBUG_ESP_DATA */
+/* #define DEBUG_ESP_QUEUE */
+/* #define DEBUG_ESP_DISCONNECT */
+/* #define DEBUG_ESP_STATUS */
+/* #define DEBUG_ESP_PHASES */
+/* #define DEBUG_ESP_WORKBUS */
+/* #define DEBUG_STATE_MACHINE */
+/* #define DEBUG_ESP_CMDS */
+/* #define DEBUG_ESP_IRQS */
+/* #define DEBUG_SDTR */
+/* #define DEBUG_ESP_SG */
+
+/* Use the following to sprinkle debugging messages in a way whic
+ * suits you if combinations of the above become too verbose when
+ * trying to track down a specific problem.
+ */
+/* #define DEBUG_ESP_MISC */
+
+#if defined(DEBUG_ESP)
+#define ESPLOG(foo)  printk foo
+#else
+#define ESPLOG(foo)
+#endif /* (DEBUG_ESP) */
+
+#if defined(DEBUG_ESP_DATA)
+#define ESPDATA(foo)  printk foo
+#else
+#define ESPDATA(foo)
+#endif
+
+#if defined(DEBUG_ESP_QUEUE)
+#define ESPQUEUE(foo)  printk foo
+#else
+#define ESPQUEUE(foo)
+#endif
+
+#if defined(DEBUG_ESP_DISCONNECT)
+#define ESPDISC(foo)  printk foo
+#else
+#define ESPDISC(foo)
+#endif
+
+#if defined(DEBUG_ESP_STATUS)
+#define ESPSTAT(foo)  printk foo
+#else
+#define ESPSTAT(foo)
+#endif
+
+#if defined(DEBUG_ESP_PHASES)
+#define ESPPHASE(foo)  printk foo
+#else
+#define ESPPHASE(foo)
+#endif
+
+#if defined(DEBUG_ESP_WORKBUS)
+#define ESPBUS(foo)  printk foo
+#else
+#define ESPBUS(foo)
+#endif
+
+#if defined(DEBUG_ESP_IRQS)
+#define ESPIRQ(foo)  printk foo
+#else
+#define ESPIRQ(foo)
+#endif
+
+#if defined(DEBUG_SDTR)
+#define ESPSDTR(foo)  printk foo
+#else
+#define ESPSDTR(foo)
+#endif
+
+#if defined(DEBUG_ESP_MISC)
+#define ESPMISC(foo)  printk foo
+#else
+#define ESPMISC(foo)
+#endif
+
+#define INTERNAL_ESP_ERROR \
+        (panic ("Internal ESP driver error in file %s, line %d\n", \
+		__FILE__, __LINE__))
+
+#define INTERNAL_ESP_ERROR_NOPANIC \
+        (printk ("Internal ESP driver error in file %s, line %d\n", \
+		 __FILE__, __LINE__))
 
 /* The ESP SCSI controllers have their register sets in three
  * "classes":
@@ -24,7 +110,7 @@
  * apart with a big-endian ordering to the bytes.
  */
 
-struct Sparc_ESP_regs {
+struct ESP_regs {
                                 /* Access    Description              Offset */
     volatile unchar esp_tclow;  /* rw  Low bits of the transfer count 0x00   */
                                 unchar tlpad1[3];
@@ -68,7 +154,7 @@
 
 /* Various revisions of the ESP board. */
 enum esp_rev {
-  esp100     = 0x00,  /* NCR53C90  */
+  esp100     = 0x00,  /* NCR53C90 - very broken */
   esp100a    = 0x01,  /* NCR53C90A */
   esp236     = 0x02,
   fas236     = 0x03,
@@ -79,16 +165,16 @@
 
 /* We get one of these for each ESP probed. */
 struct Sparc_ESP {
-  struct Sparc_ESP *next;           /* Next ESP on probed or NULL */
-  struct Sparc_ESP_regs *eregs;           /* All esp registers */
-  struct Linux_SBus_DMA *dma;             /* Who I do transfers with. */
-  struct sparc_dma_registers *dregs;      /* And his registers. */
+  struct Sparc_ESP *next;                 /* Next ESP on probed or NULL */
+  struct ESP_regs *eregs;	          /* All esp registers */
+  struct Linux_DMA *dma;                  /* Who I do transfers with. */
+  void *dregs;		  		  /* And his registers. */
   struct Scsi_Host *ehost;                /* Backpointer to SCSI Host */
 
-  struct linux_sbus_device *edev;         /* Pointer to SBus entry */
+  void *edev;        		          /* Pointer to controller base/SBus */
   char prom_name[64];                     /* Name of ESP device from prom */
   int prom_node;                          /* Prom node where ESP found */
-  int esp_id;                             /* Same as esphost->host_id */
+  int esp_id;                             /* Unique per-ESP ID number */
 
   /* ESP Configuration Registers */
   unsigned char config1;                  /* Copy of the 1st config register */
@@ -98,13 +184,17 @@
   /* The current command we are sending to the ESP chip.  This esp_command
    * ptr needs to be mapped in DVMA area so we can send commands and read
    * from the ESP fifo without burning precious CPU cycles.  Programmed I/O
-   * sucks when we have the DVMA to do it for us.
+   * sucks when we have the DVMA to do it for us.  The ESP is stupid and will
+   * only send out 6, 10, and 12 byte SCSI commands, others we need to send
+   * one byte at a time.  esp_slowcmd being set says that we are doing one
+   * of the command types ESP doesn't understand, esp_scmdp keeps track of
+   * which byte we are sending, esp_scmdleft says how many bytes to go.
    */
   volatile unchar *esp_command;           /* Location of command */
-  int esp_clen;                           /* Length of this command */
-
-  /* To hold onto the dvma buffer ptr. */
-  char *dvma_hold;
+  unsigned char esp_clen;                 /* Length of this command */
+  unsigned char esp_slowcmd;
+  unsigned char *esp_scmdp;
+  unsigned char esp_scmdleft;
 
   /* The following are used to determine the cause of an IRQ. Upon every
    * IRQ entry we synchronize these with the hardware registers.
@@ -118,14 +208,34 @@
   unsigned int cfact;                    /* Clock conversion factor */
   unsigned int ccycle;                   /* One ESP clock cycle */
   unsigned int ctick;                    /* One ESP clock time */
-  unsigned int sync_defp;                /* Default negotiation period */
+  unsigned int radelay;                  /* FAST chip req/ack delay */
+  unsigned int neg_defp;                 /* Default negotiation period */
+  unsigned int sync_defp;                /* Default sync transfer period */
+  unsigned int max_period;               /* longest our period can be */
+  unsigned int min_period;               /* shortest period we can withstand */
+  unsigned char ccf;			 /* Clock conversion factor */
+  /* For slow to medium speed input clock rates we shoot for 5mb/s,
+   * but for high input clock rates we try to do 10mb/s although I
+   * don't think a transfer can even run that fast with an ESP even
+   * with DMA2 scatter gather pipelining.
+   */
+#define SYNC_DEFP_SLOW            0x32   /* 5mb/s  */
+#define SYNC_DEFP_FAST            0x19   /* 10mb/s */
+
+  unsigned int snip;                      /* Sync. negotiation in progress */
+  unsigned int targets_present;           /* targets spoken to before */
+
+  int current_transfer_size;              /* Set at beginning of data dma */
+
+  unchar espcmdlog[32];                   /* Log of current esp cmds sent. */
+  unchar espcmdent;                       /* Current entry in esp cmd log. */
 
   /* Misc. info about this ESP */
   enum esp_rev erev;                      /* ESP revision */
   int irq;                                /* SBus IRQ for this ESP */
   int scsi_id;                            /* Who am I as initiator? */
   int scsi_id_mask;                       /* Bitmask of 'me'. */
-  int diff;                               /* Differential SCSI? */
+  int diff;                               /* Differential SCSI bus? */
   int bursts;                             /* Burst sizes our DVMA supports */
 
   /* Our command queues, only one cmd lives in the current_SC queue. */
@@ -137,8 +247,45 @@
   Scsi_Cmnd *eatme_SC;           /* Cmds waiting for esp thread to process. */
 #endif
 
-  /* Abortion status */
-  int aborting, abortion_complete, abort_result;
+  /* Message goo */
+  unchar cur_msgout[16];
+  unchar cur_msgin[16];
+  unchar prevmsgout, prevmsgin;
+  unchar msgout_len, msgin_len;
+  unchar msgout_ctr, msgin_ctr;
+
+  /* States that we cannot keep in the per cmd structure because they
+   * cannot be assosciated with any specific command.
+   */
+  unchar being_reselected;
+  unchar resetting_bus;
+  unchar aborting_cmd;
+  unchar aborting_all;
+
+  /* Functions handling DMA
+   */ 
+  /* Required functions */
+  int  (*dma_bytes_sent)(struct Sparc_ESP *, int);
+  int  (*dma_can_transfer)(struct Sparc_ESP *, Scsi_Cmnd *);
+  void (*dma_dump_state)(struct Sparc_ESP *);
+  void (*dma_init_read)(struct Sparc_ESP *, char *, int);
+  void (*dma_init_write)(struct Sparc_ESP *, char *, int);
+  void (*dma_ints_off)(struct Sparc_ESP *);
+  void (*dma_ints_on)(struct Sparc_ESP *);
+  int  (*dma_irq_p)(struct Sparc_ESP *);
+  int  (*dma_ports_p)(struct Sparc_ESP *);
+  void (*dma_setup)(struct Sparc_ESP *, char *, int, int);
+
+  /* Optional functions (i.e. may be initialized to 0) */
+  void (*dma_barrier)(struct Sparc_ESP *);
+  void (*dma_drain)(struct Sparc_ESP *);
+  void (*dma_invalidate)(struct Sparc_ESP *);
+  void (*dma_irq_entry)(struct Sparc_ESP *);
+  void (*dma_irq_exit)(struct Sparc_ESP *);
+  void (*dma_led_off)(struct Sparc_ESP *);
+  void (*dma_led_on)(struct Sparc_ESP *);
+  void (*dma_poll)(struct Sparc_ESP *, unsigned char *);
+  void (*dma_reset)(struct Sparc_ESP *);
 };
 
 /* Bitfield meanings for the above registers. */
@@ -283,7 +430,12 @@
 #define ESP_STEP_PPC          0x03             /* Early phase chg caused cmnd
                                                 * bytes to be lost
                                                 */
-#define ESP_STEP_FINI         0x04             /* Command was sent ok */
+#define ESP_STEP_FINI4        0x04             /* Command was sent ok */
+
+/* Ho hum, some ESP's set the step register to this as well... */
+#define ESP_STEP_FINI5        0x05
+#define ESP_STEP_FINI6        0x06
+#define ESP_STEP_FINI7        0x07
 
 /* ESP chip-test register read-write */
 #define ESP_TEST_TARG         0x01             /* Target test mode */
@@ -299,6 +451,7 @@
 /* ESP fifo flags register read-only */
 /* Note that the following implies a 16 byte FIFO on the ESP. */
 #define ESP_FF_FBYTES         0x1f             /* Num bytes in FIFO */
+#define ESP_FF_ONOTZERO       0x20             /* offset ctr not zero (esp100) */
 #define ESP_FF_SSTEP          0xe0             /* Sequence step */
 
 /* ESP clock conversion factor register write-only */
@@ -311,40 +464,25 @@
 #define ESP_CCF_F6            0x06             /* 25.01MHz - 30MHz */
 #define ESP_CCF_F7            0x07             /* 30.01MHz - 35MHz */
 
-extern int esp_detect(struct SHT *);
-extern const char *esp_info(struct Scsi_Host *);
-extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-extern int esp_command(Scsi_Cmnd *);
-extern int esp_abort(Scsi_Cmnd *);
-extern int esp_reset(Scsi_Cmnd *, unsigned int);
+#define ESP_BUS_TIMEOUT        275             /* In milli-seconds */
+#define ESP_TIMEO_CONST       8192
+#define ESP_NEG_DEFP(mhz, cfact) \
+        ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact)))
+#define ESP_MHZ_TO_CYCLE(mhertz)  ((1000000000) / ((mhertz) / 1000))
+#define ESP_TICK(ccf, cycle)  ((7682 * (ccf) * (cycle) / 1000))
 
-extern struct proc_dir_entry proc_scsi_esp;
 
-#define SCSI_SPARC_ESP {                                                               \
-/* struct SHT *next */                                         NULL,                   \
-/* long *usage_count */                                        NULL,                   \
-/* struct proc_dir_entry *proc_dir */                          &proc_scsi_esp,         \
-/* int (*proc_info)(char *, char **, off_t, int, int, int) */  NULL,                   \
-/* const char *name */                                         "Sun ESP 100/100a/200", \
-/* int detect(struct SHT *) */                                 esp_detect,             \
-/* int release(struct Scsi_Host *) */                          NULL,                   \
-/* const char *info(struct Scsi_Host *) */                     esp_info,               \
-/* int command(Scsi_Cmnd *) */                                 esp_command,            \
-/* int queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)) */ esp_queue,              \
-/* int abort(Scsi_Cmnd *) */                                   esp_abort,              \
-/* int reset(Scsi_Cmnd *, int) */                              esp_reset,              \
-/* int slave_attach(int, int) */                               NULL,                   \
-/* int bios_param(Disk *, kdev_t, int[]) */                    NULL,                   \
-/* int can_queue */                                            10,                     \
-/* int this_id */                                              7,                      \
-/* short unsigned int sg_tablesize */                          SG_ALL,                 \
-/* short cmd_per_lun */                                        1,                      \
-/* unsigned char present */                                    0,                      \
-/* unsigned unchecked_isa_dma:1 */                             0,                      \
-/* unsigned use_clustering:1 */                                DISABLE_CLUSTERING, }
+extern struct proc_dir_entry proc_scsi_esp;
+extern int nesps, esps_in_use, esps_running;
 
 /* For our interrupt engine. */
 #define for_each_esp(esp) \
         for((esp) = espchain; (esp); (esp) = (esp)->next)
 
-#endif /* !(_SPARC_ESP_H) */
+
+/* External functions */
+extern struct Sparc_ESP *esp_allocate(Scsi_Host_Template *, 
+				      unsigned int, void *);
+extern void esp_initialize(struct Sparc_ESP *);
+extern void esp_intr(int, void *, struct pt_regs *);
+#endif /* !(_ESP_H) */
diff -urN pc-2.0.21/drivers/scsi/gvp11.c linux-2.0.21/drivers/scsi/gvp11.c
--- pc-2.0.21/drivers/scsi/gvp11.c	Fri Apr 26 09:30:51 1996
+++ linux-2.0.21/drivers/scsi/gvp11.c	Thu Sep 26 19:20:39 1996
@@ -1,11 +1,12 @@
 #include <linux/types.h>
 #include <linux/mm.h>
 #include <linux/blk.h>
+#include <linux/sched.h>
 #include <linux/version.h>
 
+#include <asm/setup.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
-#include <asm/bootinfo.h>
 #include <asm/amigaints.h>
 #include <asm/amigahw.h>
 #include <asm/zorro.h>
@@ -29,7 +30,7 @@
 static struct Scsi_Host *first_instance = NULL;
 static Scsi_Host_Template *gvp11_template;
 
-static void gvp11_intr (int irq, struct pt_regs *fp, void *dummy)
+static void gvp11_intr (int irq, void *dummy, struct pt_regs *fp)
 {
     unsigned int status;
     struct Scsi_Host *instance;
@@ -77,34 +78,50 @@
 	    & ~0x1ff;
 	HDATA(cmd->host)->dma_bounce_buffer =
 	    scsi_malloc (HDATA(cmd->host)->dma_bounce_len);
+	HDATA(cmd->host)->dma_buffer_pool = BUF_SCSI_ALLOCED;
 	
-	/* can't allocate memory; use PIO */
 	if (!HDATA(cmd->host)->dma_bounce_buffer) {
-	    HDATA(cmd->host)->dma_bounce_len = 0;
-	    return 1;
+	    HDATA(cmd->host)->dma_bounce_buffer =
+		amiga_chip_alloc(HDATA(cmd->host)->dma_bounce_len);
+
+	    if(!HDATA(cmd->host)->dma_bounce_buffer)
+	    {
+		HDATA(cmd->host)->dma_bounce_len = 0;
+		return 1;
+	    }
+
+	    HDATA(cmd->host)->dma_buffer_pool = BUF_CHIP_ALLOCED;
 	}
 
 	/* check if the address of the bounce buffer is OK */
 	addr = VTOP(HDATA(cmd->host)->dma_bounce_buffer);
 
 	if (addr & gvp11_xfer_mask) {
-	    scsi_free (HDATA(cmd->host)->dma_bounce_buffer,
-		       HDATA(cmd->host)->dma_bounce_len);
-	    HDATA(cmd->host)->dma_bounce_buffer = NULL;
-	    HDATA(cmd->host)->dma_bounce_len = 0;
-	    return 1;
+	    /* fall back to Chip RAM if address out of range */
+	    if( HDATA(cmd->host)->dma_buffer_pool == BUF_SCSI_ALLOCED)
+		scsi_free (HDATA(cmd->host)->dma_bounce_buffer,
+			   HDATA(cmd->host)->dma_bounce_len);
+	    else
+		amiga_chip_free (HDATA(cmd->host)->dma_bounce_buffer);
+		
+	    HDATA(cmd->host)->dma_bounce_buffer =
+		amiga_chip_alloc(HDATA(cmd->host)->dma_bounce_len);
+
+	    if(!HDATA(cmd->host)->dma_bounce_buffer)
+	    {
+		HDATA(cmd->host)->dma_bounce_len = 0;
+		return 1;
+	    }
+
+	    addr = VTOP(HDATA(cmd->host)->dma_bounce_buffer);
+	    HDATA(cmd->host)->dma_buffer_pool = BUF_CHIP_ALLOCED;
 	}
 	    
 	if (!dir_in) {
 	    /* copy to bounce buffer for a write */
 	    if (cmd->use_sg)
-#if 0
-		panic ("scsi%ddma: incomplete s/g support",
-		       cmd->host->host_no);
-#else
 		memcpy (HDATA(cmd->host)->dma_bounce_buffer,
 				cmd->SCp.ptr, cmd->SCp.this_residual);
-#endif
 	    else
 		memcpy (HDATA(cmd->host)->dma_bounce_buffer,
 			cmd->request_buffer, cmd->request_bufflen);
@@ -154,8 +171,13 @@
 		memcpy (SCpnt->SCp.ptr, 
 			HDATA(instance)->dma_bounce_buffer,
 			SCpnt->SCp.this_residual);
-	    scsi_free (HDATA(instance)->dma_bounce_buffer,
-		       HDATA(instance)->dma_bounce_len);
+
+	    if (HDATA(instance)->dma_buffer_pool == BUF_SCSI_ALLOCED)
+		scsi_free (HDATA(instance)->dma_bounce_buffer,
+			    HDATA(instance)->dma_bounce_len);
+	    else
+		amiga_chip_free(HDATA(instance)->dma_bounce_buffer);
+
 	    HDATA(instance)->dma_bounce_buffer = NULL;
 	    HDATA(instance)->dma_bounce_len = 0;
 	    
@@ -166,8 +188,12 @@
 			HDATA(instance)->dma_bounce_buffer,
 			SCpnt->request_bufflen);
 
-	    scsi_free (HDATA(instance)->dma_bounce_buffer,
-		       HDATA(instance)->dma_bounce_len);
+	    if (HDATA(instance)->dma_buffer_pool == BUF_SCSI_ALLOCED)
+		scsi_free (HDATA(instance)->dma_bounce_buffer,
+			    HDATA(instance)->dma_bounce_len);
+	    else
+		amiga_chip_free(HDATA(instance)->dma_bounce_buffer);
+
 	    HDATA(instance)->dma_bounce_buffer = NULL;
 	    HDATA(instance)->dma_bounce_len = 0;
 	}
@@ -178,69 +204,67 @@
 {
     static unsigned char called = 0;
     struct Scsi_Host *instance;
-    int i, manuf, product, num_gvp11 = 0;
+    int num_gvp11 = 0;
     caddr_t address;
     enum GVP_ident epc;
+    int key;
+    struct ConfigDev *cd;
 
     if (!MACH_IS_AMIGA || called)
 	return 0;
     called = 1;
 
     tpnt->proc_dir = &proc_scsi_gvp11;
+    tpnt->proc_info = &wd33c93_proc_info;
 
-    for (i = 0; i < boot_info.bi_amiga.num_autocon; i++)
-    {
-	manuf = boot_info.bi_amiga.autocon[i].cd_Rom.er_Manufacturer;
-	product = boot_info.bi_amiga.autocon[i].cd_Rom.er_Product;
-#if 0
-/* this seems to catch some non HD boards.  GVP is sooooo stupid */
-	if (manuf == MANUF_GVP &&
-	    ((product == PROD_GVPIISCSI) || (product == PROD_GVPIISCSI_2))) {
-#else
-        if (manuf == MANUF_GVP && product == PROD_GVPIISCSI) {
-#endif
-	    /* check extended product code */
-	    address = boot_info.bi_amiga.autocon[i].cd_BoardAddr;
-	    epc = *(unsigned short *)(ZTWO_VADDR(address) + 0x8000);
-
-	    epc = epc & GVP_EPCMASK;
-
-	    /* 
-	     * This should (hopefully) be the correct way to identify
-	     * all the different GVP SCSI controllers (except for the
-	     * SERIES I though).
-	     */
-	    if (!((epc == GVP_A1291_SCSI) || 
-		  (epc == GVP_GFORCE_040_SCSI) ||
-		  (epc == GVP_GFORCE_030_SCSI) ||
-		  (epc == GVP_A530_SCSI) ||
-		  (epc == GVP_COMBO_R4_SCSI) ||
-		  (epc == GVP_COMBO_R3_SCSI) ||
-		  (epc == GVP_SERIESII)))
-		continue;
-
-	    instance = scsi_register (tpnt,
-				      sizeof (struct WD33C93_hostdata));
-	    instance->base = (unsigned char *)ZTWO_VADDR(address);
-	    DMA(instance)->secret2 = 1;
-	    DMA(instance)->secret1 = 0;
-	    DMA(instance)->secret3 = 15;
-	    while (DMA(instance)->CNTR & GVP11_DMAC_BUSY) ;
-	    DMA(instance)->CNTR = 0;
-	    wd33c93_init(instance, (wd33c93_regs *)&(DMA(instance)->SASR),
-			 dma_setup, dma_stop, WD33C93_FS_8_10);
-	    if (num_gvp11++ == 0) {
-		first_instance = instance;
-		gvp11_template = instance->hostt;
-		add_isr(IRQ_AMIGA_PORTS, gvp11_intr, 0, NULL, "GVP11 SCSI");
-	    }
-	    DMA(instance)->CNTR = GVP11_DMAC_INT_ENABLE;
-
-#if 0 /* The Zorro stuff is not totally integrated yet ! */
-	    boot_info.bi_amiga.autocon_configured |= 1<<i;
-#endif
+    while ((key = zorro_find(MANUF_GVP, PROD_GVPIISCSI, 0, 0))) {
+	cd = zorro_get_board(key);
+	address = cd->cd_BoardAddr;
+	/* check extended product code */
+	epc = *(unsigned short *)(ZTWO_VADDR(address) + 0x8000);
+	epc = epc & GVP_PRODMASK;
+
+	/* 
+	 * This should (hopefully) be the correct way to identify
+	 * all the different GVP SCSI controllers (except for the
+	 * SERIES I though).
+	 */
+	if (!((epc == GVP_A1291_SCSI) || 
+	      (epc == GVP_GFORCE_040_SCSI) ||
+	      (epc == GVP_GFORCE_030_SCSI) ||
+	      (epc == GVP_A530_SCSI) ||
+	      (epc == GVP_COMBO_R4_SCSI) ||
+	      (epc == GVP_COMBO_R3_SCSI) ||
+	      (epc == GVP_SERIESII)))
+	    continue;
 
+	instance = scsi_register (tpnt, sizeof (struct WD33C93_hostdata));
+	instance->base = (unsigned char *)ZTWO_VADDR(address);
+	instance->irq = IRQ_AMIGA_PORTS & ~IRQ_MACHSPEC;
+	DMA(instance)->secret2 = 1;
+	DMA(instance)->secret1 = 0;
+	DMA(instance)->secret3 = 15;
+	while (DMA(instance)->CNTR & GVP11_DMAC_BUSY) ;
+	DMA(instance)->CNTR = 0;
+
+	epc = *(unsigned short *)(ZTWO_VADDR(address) + 0x8000);
+	/*
+	 * Check for 14MHz SCSI clock
+	 */
+	if (epc & GVP_SCSICLKMASK)
+	  wd33c93_init(instance, (wd33c93_regs *)&(DMA(instance)->SASR),
+		       dma_setup, dma_stop, WD33C93_FS_8_10);
+	else
+	  wd33c93_init(instance, (wd33c93_regs *)&(DMA(instance)->SASR),
+		       dma_setup, dma_stop, WD33C93_FS_12_15);
+
+	if (num_gvp11++ == 0) {
+	    first_instance = instance;
+	    gvp11_template = instance->hostt;
+	    request_irq(IRQ_AMIGA_PORTS, gvp11_intr, 0, "GVP11 SCSI", gvp11_intr);
 	}
+	DMA(instance)->CNTR = GVP11_DMAC_INT_ENABLE;
+	zorro_config_board(key, 0);
     }
 
     return num_gvp11;
diff -urN pc-2.0.21/drivers/scsi/hosts.c linux-2.0.21/drivers/scsi/hosts.c
--- pc-2.0.21/drivers/scsi/hosts.c	Wed Aug  7 11:31:21 1996
+++ linux-2.0.21/drivers/scsi/hosts.c	Sat Sep 14 12:55:37 1996
@@ -53,6 +53,10 @@
 #include "gvp11.h"
 #endif
 
+#ifdef CONFIG_CYBERSTORM_SCSI
+#include "cyber_esp.h"
+#endif
+
 #ifdef CONFIG_ATARI_SCSI
 #include "atari_scsi.h"
 #endif
@@ -171,7 +175,7 @@
 
 
 /*
-static const char RCSid[] = "$Header: /usr/src/linux-1.3.95/drivers/scsi/RCS/hosts.c,v 1.7 1996/04/25 22:21:56 root Exp root $";
+static const char RCSid[] = "$Header: /usr/src/cvsroot/linux/drivers/scsi/hosts.c,v 1.1 1996/08/24 12:38:46 root Exp $";
 */
 
 /*
@@ -212,6 +216,9 @@
 #endif
 #ifdef CONFIG_GVP11_SCSI
 	GVP11_SCSI,
+#endif
+#ifdef CONFIG_CYBERSTORM_SCSI
+	SCSI_CYBER_ESP,
 #endif
 #endif
 
diff -urN pc-2.0.21/drivers/scsi/scsi.c linux-2.0.21/drivers/scsi/scsi.c
--- pc-2.0.21/drivers/scsi/scsi.c	Mon Sep 16 12:05:18 1996
+++ linux-2.0.21/drivers/scsi/scsi.c	Mon Sep 23 22:31:39 1996
@@ -63,7 +63,7 @@
 #undef USE_STATIC_SCSI_MEMORY
 
 /*
-static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/scsi.c,v 1.5 1993/09/24 12:45:18 drew Exp drew $";
+static const char RCSid[] = "$Header: /root/linux/drivers/scsi/RCS/scsi.c,v 1.6 1996/04/17 23:39:23 root Exp $";
 */
 
 
@@ -88,7 +88,7 @@
 #endif
 
 static void scsi_done (Scsi_Cmnd *SCpnt);
-static int update_timeout (Scsi_Cmnd *, int);
+int scsi_update_timeout (Scsi_Cmnd *, int);
 static void print_inquiry(unsigned char *data);
 static void scsi_times_out (Scsi_Cmnd * SCpnt);
 static int scan_scsis_single (int channel,int dev,int lun,int * max_scsi_dev ,
@@ -865,8 +865,9 @@
 	SCpnt->internal_timeout |= IN_RESET2;
         scsi_reset (SCpnt,
 		    SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_BUS_RESET);
-        return;
-    case (IN_ABORT | IN_RESET | IN_RESET2):
+	return;
+    case IN_RESET2:
+    case (IN_ABORT | IN_RESET2):
 	/* Obviously the bus reset didn't work.
 	 * Let's try even harder and call for an HBA reset.
          * Maybe the HBA itself crashed and this will shake it loose.
@@ -1228,7 +1229,7 @@
     }
     restore_flags(flags);
     
-    update_timeout(SCpnt, SCpnt->timeout_per_command);
+    scsi_update_timeout(SCpnt, SCpnt->timeout_per_command);
     
     /*
      * We will use a queued command if possible, otherwise we will emulate the
@@ -1293,7 +1294,7 @@
     save_flags(flags);
     cli();
     SCpnt->flags |= WAS_SENSE | ASKED_FOR_SENSE;
-    update_timeout(SCpnt, SENSE_TIMEOUT);
+    scsi_update_timeout(SCpnt, SENSE_TIMEOUT);
     restore_flags(flags);
     
     
@@ -1510,7 +1511,7 @@
     struct Scsi_Host * host = SCpnt->host;
     int result = SCpnt->result;
     SCpnt->serial_number = 0;
-    oldto = update_timeout(SCpnt, 0);
+    oldto = scsi_update_timeout(SCpnt, 0);
     
 #ifdef DEBUG_TIMEOUT
     if(result) printk("Non-zero result in scsi_done %x %d:%d\n",
@@ -1590,7 +1591,7 @@
 #ifdef DEBUG
 			printk("NO SENSE.  status = REDO\n");
 #endif
-			update_timeout(SCpnt, oldto);
+			scsi_update_timeout(SCpnt, oldto);
 			status = REDO;
 			break;
 		    case SUGGEST_IS_OK:
@@ -1637,7 +1638,7 @@
 		switch (check_sense(SCpnt))
 		{
 		case 0:
-		    update_timeout(SCpnt, oldto);
+		    scsi_update_timeout(SCpnt, oldto);
 		    status = REDO;
 		    break;
 		case SUGGEST_REMAP:
@@ -1666,7 +1667,7 @@
 		
 	    case BUSY:
 	    case QUEUE_FULL:
-		update_timeout(SCpnt, oldto);
+		scsi_update_timeout(SCpnt, oldto);
 		status = REDO;
 		break;
 		
@@ -1751,7 +1752,7 @@
 	   status_byte(result) == CHECK_CONDITION) {
 	    switch (check_sense(SCpnt)) {
 	    case 0:
-		update_timeout(SCpnt, oldto);
+		scsi_update_timeout(SCpnt, oldto);
 		status = REDO;
 		break;
 	    case SUGGEST_REMAP:
@@ -1905,7 +1906,7 @@
 	else
 	{
 	    SCpnt->internal_timeout |= IN_ABORT;
-	    oldto = update_timeout(SCpnt, ABORT_TIMEOUT);
+	    oldto = scsi_update_timeout(SCpnt, ABORT_TIMEOUT);
 	    
 	    if ((SCpnt->flags & IS_RESETTING) && SCpnt->device->soft_reset) {
 		/* OK, this command must have died when we did the
@@ -1919,7 +1920,7 @@
 	    restore_flags(flags);
 	    if (!host->host_busy) {
 		SCpnt->internal_timeout &= ~IN_ABORT;
-		update_timeout(SCpnt, oldto);
+		scsi_update_timeout(SCpnt, oldto);
 		return 0;
 	    }
 	    printk("scsi : aborting command due to timeout : pid %lu, scsi%d,"
@@ -1952,7 +1953,7 @@
 		    } else {
 			SCpnt->flags |= WAS_TIMEDOUT;
 			oldto = SCpnt->timeout_per_command;
-			update_timeout(SCpnt, oldto);
+			scsi_update_timeout(SCpnt, oldto);
 		    }
 		    restore_flags(flags);
 		}
@@ -1961,7 +1962,7 @@
 		if(why != DID_TIME_OUT) {
 		    save_flags(flags);
 		    cli();
-		    update_timeout(SCpnt, oldto);
+		    scsi_update_timeout(SCpnt, oldto);
 		    restore_flags(flags);
 		}
 		return 0;
@@ -1973,7 +1974,7 @@
                  return 0;
 	    case SCSI_ABORT_NOT_RUNNING:
 		SCpnt->internal_timeout &= ~IN_ABORT;
-		update_timeout(SCpnt, 0);
+		scsi_update_timeout(SCpnt, 0);
 		return 0;
 	    case SCSI_ABORT_ERROR:
 	    default:
@@ -2096,7 +2097,7 @@
 	else
 	{
 	    SCpnt->internal_timeout |= IN_RESET;
-	    update_timeout(SCpnt, RESET_TIMEOUT);
+	    scsi_update_timeout(SCpnt, RESET_TIMEOUT);
 	    
 	    if (host->host_busy)
 	    {
@@ -2223,7 +2224,7 @@
 		save_flags(flags);
 		cli();
 		SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3);
-		update_timeout(SCpnt, 0);
+		scsi_update_timeout(SCpnt, 0);
 		restore_flags(flags);
 		/* If you snooze, you lose... */
 	    case SCSI_RESET_ERROR:
@@ -2251,7 +2252,7 @@
     save_flags(flags);
     cli();
 
-    update_timeout(NULL, 0);
+    scsi_update_timeout(NULL, 0);
 
     /*
      * Find all timers such that they have 0 or negative (shouldn't happen)
@@ -2291,7 +2292,7 @@
  * set the timer, we want to take this value into account.
  */
 
-static int update_timeout(Scsi_Cmnd * SCset, int timeout)
+int scsi_update_timeout(Scsi_Cmnd * SCset, int timeout)
 {
     unsigned int least, used;
     unsigned int oldto;
diff -urN pc-2.0.21/drivers/scsi/scsi.h linux-2.0.21/drivers/scsi/scsi.h
--- pc-2.0.21/drivers/scsi/scsi.h	Mon Jul  8 09:41:07 1996
+++ linux-2.0.21/drivers/scsi/scsi.h	Thu Sep 26 23:09:51 1996
@@ -236,7 +236,13 @@
 #else
 # define ISA_DMA_THRESHOLD (0x00ffffff)
 #endif
+#ifdef __mc68000__
+#include <asm/pgtable.h>
+#define CONTIGUOUS_BUFFERS(X,Y) \
+	(VTOP((X)->b_data+(X)->b_size-1)+1 == VTOP((Y)->b_data))
+#else
 #define CONTIGUOUS_BUFFERS(X,Y) ((X->b_data+X->b_size) == Y->b_data)
+#endif
 
 
 /*
diff -urN pc-2.0.21/drivers/scsi/wd33c93.c linux-2.0.21/drivers/scsi/wd33c93.c
--- pc-2.0.21/drivers/scsi/wd33c93.c	Sat May  4 18:39:23 1996
+++ linux-2.0.21/drivers/scsi/wd33c93.c	Tue Sep 24 19:30:38 1996
@@ -89,12 +89,12 @@
 #endif
 
 /* Leave this undefined for now - need to make some changes in the
- * a3000/a2019/gvp11 files to get it working right
+ * a3000/a2091/gvp11 files to get it working right
  */
-/*#define PROC_INTERFACE*/     /* add code for /proc/scsi/wd33c93/xxx interface */
+#define PROC_INTERFACE     /* add code for /proc/scsi/wd33c93/xxx interface */
 
-#define SYNC_DEBUG         /* extra info on sync negotiation printed */
-#define DEBUGGING_ON       /* enable command-line debugging bitmask */
+#undef SYNC_DEBUG          /* extra info on sync negotiation printed */
+#undef DEBUGGING_ON        /* enable command-line debugging bitmask */
 #define DEBUG_DEFAULTS 0   /* default debugging bitmask */
 
 #define WD33C93_VERSION    "1.21"
@@ -106,6 +106,11 @@
 #define DB(f,a)
 #endif
 
+#define SEL_IRQ 1
+#ifdef SEL_IRQ
+#include <asm/irq.h>
+#endif
+
 #define IS_DIR_OUT(cmd) ((cmd)->cmnd[0] == WRITE_6  || \
                          (cmd)->cmnd[0] == WRITE_10 || \
                          (cmd)->cmnd[0] == WRITE_12)
@@ -271,11 +276,15 @@
 {
 struct WD33C93_hostdata *hostdata;
 Scsi_Cmnd *tmp;
-unsigned long flags;
-
 
+#ifdef SEL_IRQ
+   unsigned char irq = cmd->host->irq;
+   disable_irq(irq);
+#else
+   unsigned long flags;
    save_flags(flags);
    cli();
+#endif
    hostdata = (struct WD33C93_hostdata *)cmd->host->hostdata;
 
 DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld( ",cmd->target,cmd->cmnd[0],cmd->pid))
@@ -347,7 +356,11 @@
 
 DB(DB_QUEUE_COMMAND,printk(")Q-%ld ",cmd->pid))
 
+#ifdef SEL_IRQ
+   enable_irq(irq);
+#else
    restore_flags(flags);
+#endif
    return 0;
 }
 
@@ -364,12 +377,16 @@
 struct WD33C93_hostdata *hostdata;
 wd33c93_regs *regp;
 Scsi_Cmnd *cmd, *prev;
-unsigned long flags;
 int i;
 
-
+#ifdef SEL_IRQ
+   disable_irq(instance->irq);
+#else
+   unsigned long flags;
    save_flags(flags);
    cli();
+#endif
+
    hostdata = (struct WD33C93_hostdata *)instance->hostdata;
    regp = hostdata->regp;
 
@@ -379,7 +396,11 @@
 
 DB(DB_EXECUTE,printk(")EX-0 "))
 
+#ifdef SEL_IRQ
+      enable_irq(instance->irq);
+#else
       restore_flags(flags);
+#endif
       return;
       }
 
@@ -403,7 +424,11 @@
 
 DB(DB_EXECUTE,printk(")EX-1 "))
 
+#ifdef SEL_IRQ
+      enable_irq(instance->irq);
+#else
       restore_flags(flags);
+#endif
       return;
       }
 
@@ -576,7 +601,11 @@
       
 DB(DB_EXECUTE,printk("%s%ld)EX-2 ",(cmd->SCp.phase)?"d:":"",cmd->pid))
 
+#ifdef SEL_IRQ
+   enable_irq(instance->irq);
+#else
    restore_flags(flags);
+#endif
 }
 
 
@@ -679,10 +708,11 @@
 struct WD33C93_hostdata *hostdata;
 Scsi_Cmnd *patch, *cmd;
 wd33c93_regs *regp;
-unsigned long flags;
 uchar asr, sr, phs, id, lun, *ucp, msg;
 unsigned long length;
-
+#ifndef SEL_IRQ
+unsigned long flags;
+#endif
 
    hostdata = (struct WD33C93_hostdata *)instance->hostdata;
    regp = hostdata->regp;
@@ -693,8 +723,11 @@
 
 /* OK - it should be safe to re-enable system interrupts */
 
+#ifndef SEL_IRQ
    save_flags(flags);
-   sti();
+#endif
+
+   /* sti(); it is fundamentally wrong to do sti() in an interrupt */
 
    cmd = (Scsi_Cmnd *)hostdata->connected;   /* assume we're connected */
    sr = read_wd33c93(regp, WD_SCSI_STATUS);  /* clear the interrupt */
@@ -734,7 +767,11 @@
       case CSR_TIMEOUT:
 DB(DB_INTR,printk("TIMEOUT"))
 
+#ifdef SEL_IRQ
+         disable_irq(instance->irq);
+#else
          cli();
+#endif
          if (hostdata->state == S_RUNNING_LEVEL2)
             hostdata->connected = NULL;
          else {
@@ -751,7 +788,11 @@
  * are commands waiting to be executed.
  */
 
-         sti();
+#ifdef SEL_IRQ
+	 enable_irq(instance->irq);
+#else
+         restore_flags(flags);
+#endif
          wd33c93_execute(instance);
          break;
 
@@ -759,7 +800,12 @@
 /* Note: this interrupt should not occur in a LEVEL2 command */
 
       case CSR_SELECT:
+#ifdef SEL_IRQ
+	 disable_irq(instance->irq);
+#else
          cli();
+#endif
+
 DB(DB_INTR,printk("SELECT"))
          hostdata->connected = cmd = (Scsi_Cmnd *)hostdata->selecting;
          hostdata->selecting = NULL;
@@ -848,7 +894,11 @@
       case CSR_SRV_REQ  |PHS_MESS_IN:
 DB(DB_INTR,printk("MSG_IN="))
 
+#ifdef SEL_IRQ
+         disable_irq(instance->irq);
+#else
          cli();
+#endif
          msg = read_1_byte(regp);
          sr = read_wd33c93(regp, WD_SCSI_STATUS);  /* clear interrupt */
 
@@ -997,7 +1047,11 @@
 /* Note: this interrupt will occur only after a LEVEL2 command */
 
       case CSR_SEL_XFER_DONE:
+#ifdef SEL_IRQ
+         disable_irq(instance->irq);
+#else
          cli();
+#endif
 
 /* Make sure that reselection is enabled at this point - it may
  * have been turned off for the command that just completed.
@@ -1022,7 +1076,11 @@
 /* We are no longer  connected to a target - check to see if
  * there are commands waiting to be executed.
  */
-
+#ifdef SEL_IRQ
+	    enable_irq(instance->irq);
+#else
+	    restore_flags(flags);
+#endif
             wd33c93_execute(instance);
             }
          else {
@@ -1081,7 +1139,11 @@
  * so we treat it as a normal command-complete-disconnect.
  */
 
+#ifdef SEL_IRQ
+         disable_irq(instance->irq);
+#else
          cli();
+#endif
 
 /* Make sure that reselection is enabled at this point - it may
  * have been turned off for the command that just completed.
@@ -1106,13 +1168,21 @@
 /* We are no longer connected to a target - check to see if
  * there are commands waiting to be executed.
  */
-
+#ifdef SEL_IRQ
+         enable_irq(instance->irq);
+#else
+	 restore_flags(flags);
+#endif
          wd33c93_execute(instance);
          break;
 
 
       case CSR_DISC:
+#ifdef SEL_IRQ
+         disable_irq(instance->irq);
+#else
          cli();
+#endif
 
 /* Make sure that reselection is enabled at this point - it may
  * have been turned off for the command that just completed.
@@ -1155,7 +1225,11 @@
 /* We are no longer connected to a target - check to see if
  * there are commands waiting to be executed.
  */
-
+#ifdef SEL_IRQ
+         enable_irq(instance->irq);
+#else
+	 restore_flags(flags);
+#endif
          wd33c93_execute(instance);
          break;
 
@@ -1163,7 +1237,11 @@
       case CSR_RESEL_AM:
 DB(DB_INTR,printk("RESEL"))
 
+#ifdef SEL_IRQ
+         disable_irq(instance->irq);
+#else
          cli();
+#endif
 
    /* First we have to make sure this reselection didn't */
    /* happen during Arbitration/Selection of some other device. */
@@ -1263,7 +1341,11 @@
          printk("--UNKNOWN INTERRUPT:%02x:%02x:%02x--",asr,sr,phs);
       }
 
+#ifdef SEL_IRQ
+   enable_irq(instance->irq);
+#else
    restore_flags(flags);
+#endif
 
 DB(DB_INTR,printk("} "))
 
@@ -1319,7 +1401,9 @@
 int wd33c93_reset(Scsi_Cmnd *SCpnt)
 #endif
 {
+#ifndef SEL_IRQ
 unsigned long flags;
+#endif
 struct Scsi_Host *instance;
 struct WD33C93_hostdata *hostdata;
 int i;
@@ -1328,8 +1412,12 @@
    hostdata = (struct WD33C93_hostdata *)instance->hostdata;
 
    printk("scsi%d: reset. ", instance->host_no);
+#ifdef SEL_IRQ
+   disable_irq(instance->irq);
+#else
    save_flags(flags);
    cli();
+#endif
 
    ((struct WD33C93_hostdata *)instance->hostdata)->dma_stop(instance,NULL,0);
    for (i = 0; i < 8; i++) {
@@ -1348,7 +1436,11 @@
 
    reset_wd33c93(instance);
    SCpnt->result = DID_RESET << 16;
+#ifdef SEL_IRQ
+   enable_irq(instance->irq);
+#else
    restore_flags(flags);
+#endif
    return 0;
 }
 
@@ -1360,10 +1452,16 @@
 struct WD33C93_hostdata *hostdata;
 wd33c93_regs *regp;
 Scsi_Cmnd *tmp, *prev;
+#ifndef SEL_IRQ
 unsigned long flags;
+#endif
 
+#ifdef SEL_IRQ
+   disable_irq(cmd->host->irq);
+#else
    save_flags (flags);
    cli();
+#endif
 
    instance = cmd->host;
    hostdata = (struct WD33C93_hostdata *)instance->hostdata;
@@ -1384,8 +1482,12 @@
          cmd->result = DID_ABORT << 16;
          printk("scsi%d: Abort - removing command %ld from input_Q. ",
            instance->host_no, cmd->pid);
-         cmd->scsi_done(cmd);
+#ifdef SEL_IRQ
+	 enable_irq(cmd->host->irq);
+#else
          restore_flags(flags);
+#endif
+         cmd->scsi_done(cmd);
          return SCSI_ABORT_SUCCESS;
          }
       prev = tmp;
@@ -1453,12 +1555,16 @@
       hostdata->connected = NULL;
       hostdata->state = S_UNCONNECTED;
       cmd->result = DID_ABORT << 16;
-      cmd->scsi_done(cmd);
 
 /*      sti();*/
       wd33c93_execute (instance);
 
+#ifdef SEL_IRQ
+      enable_irq(cmd->host->irq);
+#else
       restore_flags(flags);
+#endif
+      cmd->scsi_done(cmd);
       return SCSI_ABORT_SUCCESS;
       }
 
@@ -1474,7 +1580,11 @@
          printk("scsi%d: Abort - command %ld found on disconnected_Q - ",
                  instance->host_no, cmd->pid);
          printk("returning ABORT_SNOOZE. ");
+#ifdef SEL_IRQ
+	 enable_irq(cmd->host->irq);
+#else
          restore_flags(flags);
+#endif
          return SCSI_ABORT_SNOOZE;
          }
       tmp = (Scsi_Cmnd *)tmp->host_scribble;
@@ -1493,7 +1603,11 @@
 /*   sti();*/
    wd33c93_execute (instance);
 
+#ifdef SEL_IRQ
+   enable_irq(cmd->host->irq);
+#else
    restore_flags(flags);
+#endif
    printk("scsi%d: warning : SCSI command probably completed successfully"
       "         before abortion. ", instance->host_no);
    return SCSI_ABORT_NOT_RUNNING;
@@ -1708,7 +1822,7 @@
 int x,i;
 static int stop = 0;
 
-   for (instance=instance_list; instance; instance=instance->next) {
+   for (instance=scsi_hostlist; instance; instance=instance->next) {
       if (instance->host_no == hn)
          break;
       }
diff -urN pc-2.0.21/drivers/scsi/wd33c93.h linux-2.0.21/drivers/scsi/wd33c93.h
--- pc-2.0.21/drivers/scsi/wd33c93.h	Mon May  6 11:44:31 1996
+++ linux-2.0.21/drivers/scsi/wd33c93.h	Tue Sep 24 20:06:43 1996
@@ -229,9 +229,7 @@
     uchar            sync_xfer[8];     /* sync_xfer reg settings per target */
     uchar            sync_stat[8];     /* status of sync negotiation per target */
     uchar            no_sync;          /* bitmask: don't do sync on these targets */
-#if 0
     uchar            proc;             /* bitmask: what's in proc output */
-#endif
     };
 
 
diff -urN pc-2.0.21/drivers/sound/Makefile linux-2.0.21/drivers/sound/Makefile
--- pc-2.0.21/drivers/sound/Makefile	Sun Jun 30 10:43:41 1996
+++ linux-2.0.21/drivers/sound/Makefile	Sat Aug 24 14:39:37 1996
@@ -43,6 +43,15 @@
 .c.o:
 	$(CC) $(CFLAGS) -c $<
 
+ifeq ($(ARCH),m68k)
+  ifeq ($(CONFIG_DMASOUND),y)
+
+all:	dmasound.h sound.a
+
+OBJS := dmasound.o
+
+  endif
+else
 ifeq ($(CONFIG_SOUND),y)
 
 all:	local.h sound.a
@@ -51,6 +60,7 @@
 
 else
 all:
+endif
 endif
 
 ifndef HOSTCC
diff -urN pc-2.0.21/drivers/sound/dmasound.c linux-2.0.21/drivers/sound/dmasound.c
--- pc-2.0.21/drivers/sound/dmasound.c	Sat Jul  6 10:31:43 1996
+++ linux-2.0.21/drivers/sound/dmasound.c	Sat Aug 24 14:39:49 1996
@@ -3,7 +3,7 @@
 
 /*
 
-VoxWare compatible Atari TT DMA sound driver for 680x0 Linux
+VoxWare compatible Atari TT DMA sound driver for Linux/m68k
 
 (c) 1995 by Michael Schlueter & Michael Marte
 
@@ -62,7 +62,14 @@
 
 1996/3/9	++geert: support added for Amiga, A-law, 16-bit little endian.
 			Unification to drivers/sound/dmasound.c.
+
 1996/4/6	++Martin Mitchell: updated to 1.3 kernel.
+
+1996/6/13       ++topi: fixed things that were broken (mainly the amiga
+                        14-bit routines), /dev/sndstat shows now the real
+                        hardware frequency, the lowpass filter is disabled
+			by default now.
+
 */
 
 
@@ -75,10 +82,10 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 
+#include <asm/setup.h>
 #include <asm/system.h>
 #include <asm/irq.h>
 #include <asm/pgtable.h>
-#include <asm/bootinfo.h>
 
 #ifdef CONFIG_ATARI
 #include <asm/atarihw.h>
@@ -99,7 +106,7 @@
 
 #ifdef CONFIG_AMIGA
    /*
-    *	The minimum period for audio depends on total (for OCS/ECS/AGA)
+    *	The minimum period for audio depends on htotal (for OCS/ECS/AGA)
     *	(Imported from arch/m68k/amiga/amisound.c)
     */
 
@@ -510,7 +517,7 @@
 static int FalconSetVolume(int volume);
 static void ata_sq_play_next_frame(int index);
 static void AtaPlay(void);
-static void ata_sq_interrupt(int irq, struct pt_regs *fp, void *dummy);
+static void ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp);
 #endif /* CONFIG_ATARI */
 
 #ifdef CONFIG_AMIGA
@@ -524,7 +531,7 @@
 static int AmiSetTreble(int treble);
 static void ami_sq_play_next_frame(int index);
 static void AmiPlay(void);
-static void ami_sq_interrupt(int irq, struct pt_regs *fp, void *dummy);
+static void ami_sq_interrupt(int irq, void *dummy, struct pt_regs *fp);
 #endif /* CONFIG_AMIGA */
 
 
@@ -1304,7 +1311,7 @@
 	    *left++ = get_user(userPtr++) ^ 0x80;
 	    *right++ = get_user(userPtr++) ^ 0x80;
 	    count--;
-	}
+        }
     }
     *frameUsed += used;
     return(used);
@@ -1324,8 +1331,8 @@
 	used = count*2;
 	while (count > 0) {
 	    data = get_user(((u_short *)userPtr)++);
-	    *high = data>>8;
-	    *low = (data>>2) & 0x3f;
+	    *high++ = data>>8;
+	    *low++ = (data>>2) & 0x3f;
 	    count--;
 	}
     } else {
@@ -1337,13 +1344,13 @@
 	used = count*4;
 	while (count > 0) {
 	    data = get_user(((u_short *)userPtr)++);
-	    *lefth = data>>8;
-	    *leftl = (data>>2) & 0x3f;
+	    *lefth++ = data>>8;
+	    *leftl++ = (data>>2) & 0x3f;
 	    data = get_user(((u_short *)userPtr)++);
-	    *righth = data>>8;
-	    *rightl = (data>>2) & 0x3f;
+	    *righth++ = data>>8;
+	    *rightl++ = (data>>2) & 0x3f;
 	    count--;
-	}
+       }
     }
     *frameUsed += used;
     return(used);
@@ -1363,8 +1370,8 @@
 	used = count*2;
 	while (count > 0) {
 	    data = get_user(((u_short *)userPtr)++) ^ 0x8000;
-	    *high = data>>8;
-	    *low = (data>>2) & 0x3f;
+	    *high++ = data>>8;
+	    *low++ = (data>>2) & 0x3f;
 	    count--;
 	}
     } else {
@@ -1376,11 +1383,11 @@
 	used = count*4;
 	while (count > 0) {
 	    data = get_user(((u_short *)userPtr)++) ^ 0x8000;
-	    *lefth = data>>8;
-	    *leftl = (data>>2) & 0x3f;
+	    *lefth++ = data>>8;
+	    *leftl++ = (data>>2) & 0x3f;
 	    data = get_user(((u_short *)userPtr)++) ^ 0x8000;
-	    *righth = data>>8;
-	    *rightl = (data>>2) & 0x3f;
+	    *righth++ = data>>8;
+	    *rightl++ = (data>>2) & 0x3f;
 	    count--;
 	}
     }
@@ -1403,8 +1410,8 @@
 	while (count > 0) {
 	    data = get_user(((u_short *)userPtr)++);
 	    data = le2be16(data);
-	    *high = data>>8;
-	    *low = (data>>2) & 0x3f;
+	    *high++ = data>>8;
+	    *low++ = (data>>2) & 0x3f;
 	    count--;
 	}
     } else {
@@ -1417,12 +1424,12 @@
 	while (count > 0) {
 	    data = get_user(((u_short *)userPtr)++);
 	    data = le2be16(data);
-	    *lefth = data>>8;
-	    *leftl = (data>>2) & 0x3f;
+	    *lefth++ = data>>8;
+	    *leftl++ = (data>>2) & 0x3f;
 	    data = get_user(((u_short *)userPtr)++);
 	    data = le2be16(data);
-	    *righth = data>>8;
-	    *rightl = (data>>2) & 0x3f;
+	    *righth++ = data>>8;
+	    *rightl++ = (data>>2) & 0x3f;
 	    count--;
 	}
     }
@@ -1445,8 +1452,8 @@
 	while (count > 0) {
 	    data = get_user(((u_short *)userPtr)++);
 	    data = le2be16(data) ^ 0x8000;
-	    *high = data>>8;
-	    *low = (data>>2) & 0x3f;
+	    *high++ = data>>8;
+	    *low++ = (data>>2) & 0x3f;
 	    count--;
 	}
     } else {
@@ -1459,12 +1466,12 @@
 	while (count > 0) {
 	    data = get_user(((u_short *)userPtr)++);
 	    data = le2be16(data) ^ 0x8000;
-	    *lefth = data>>8;
-	    *leftl = (data>>2) & 0x3f;
+	    *lefth++ = data>>8;
+	    *leftl++ = (data>>2) & 0x3f;
 	    data = get_user(((u_short *)userPtr)++);
 	    data = le2be16(data) ^ 0x8000;
-	    *righth = data>>8;
-	    *rightl = (data>>2) & 0x3f;
+	    *righth++ = data>>8;
+	    *rightl++ = (data>>2) & 0x3f;
 	    count--;
 	}
     }
@@ -1550,7 +1557,8 @@
     mfp.tim_dt_a = 1;		/* Cause interrupt after first event. */
     mfp.tim_ct_a = 8;		/* Turn on event counting. */
     /* Register interrupt handler. */
-    add_isr(IRQ_MFP_TIMA, ata_sq_interrupt, IRQ_TYPE_SLOW, NULL, "DMA sound");
+    request_irq(IRQ_MFP_TIMA, ata_sq_interrupt, IRQ_TYPE_SLOW,
+                "DMA sound", ata_sq_interrupt);
     mfp.int_en_a |= 0x20;	/* Turn interrupt on. */
     mfp.int_mk_a |= 0x20;
     return(1);
@@ -1927,7 +1935,7 @@
 }
 
 
-static void ata_sq_interrupt(int irq, struct pt_regs *fp, void *dummy)
+static void ata_sq_interrupt(int irq, void *dummy, struct pt_regs *fp)
 {
 #if 0
     /* ++TeSche: if you should want to test this... */
@@ -2018,7 +2026,8 @@
     custom.dmacon = AMI_AUDIO_OFF;
 
     /* Register interrupt handler. */
-    if (!add_isr(IRQ_AMIGA_AUD0, ami_sq_interrupt, 0, NULL, "DMA sound"))
+    if (request_irq(IRQ_AMIGA_AUD0, ami_sq_interrupt, 0,
+                    "DMA sound", ami_sq_interrupt))
 	panic("Couldn't add audio interrupt");
     return(1);
 }
@@ -2047,14 +2056,16 @@
     if (period < amiga_audio_min_period) {
 	/* we would need to squeeze the sound, but we won't do that */
 	period = amiga_audio_min_period;
-	sound.hard.speed = amiga_colorclock/(period+1);
     } else if (period > 65535) {
 	period = 65535;
-	sound.hard.speed = amiga_colorclock/(period+1);
     }
+    sound.hard.speed = amiga_colorclock/(period+1);
+
     for (i = 0; i < 4; i++)
 	custom.aud[i].audper = period;
     amiga_audio_period = period;
+
+    AmiSetTreble(50);  /* recommended for newer amiga models */
 }
 
 
@@ -2113,10 +2124,10 @@
 static int AmiSetTreble(int treble)
 {
     sound.treble = treble;
-    if (treble > 50)
-	ciaa.pra |= 0x02;
-    else
+    if (treble < 50)
 	ciaa.pra &= ~0x02;
+    else
+	ciaa.pra |= 0x02;
     return(treble);
 }
 
@@ -2161,6 +2172,8 @@
 	    /* We can play pseudo 14-bit only with the maximum volume */
 	    ch3 = ch0+sq.block_size_quarter;
 	    ch2 = ch1+sq.block_size_quarter;
+	    custom.aud[2].audvol = 1;  /* we are being affected by the beeps */
+	    custom.aud[3].audvol = 1;  /* restoring volume here helps a bit */
 	    custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2);
 	    custom.aud[2].audlen = size;
 	    custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3);
@@ -2210,7 +2223,7 @@
 }
 
 
-static void ami_sq_interrupt(int irq, struct pt_regs *fp, void *dummy)
+static void ami_sq_interrupt(int irq, void *dummy, struct pt_regs *fp)
 {
     int minframes = 1;
 
diff -urN pc-2.0.21/fs/Config.in linux-2.0.21/fs/Config.in
--- pc-2.0.21/fs/Config.in	Wed Jun  5 12:24:45 1996
+++ linux-2.0.21/fs/Config.in	Thu Sep 26 21:17:59 1996
@@ -16,6 +16,9 @@
 dep_tristate 'MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
 dep_tristate 'VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
 dep_tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
+if [ "$ARCH" = "m68k" ]; then
+  bool 'MSDOS partition support' CONFIG_MSDOS_PARTITION
+fi
 
 bool '/proc filesystem support' CONFIG_PROC_FS
 if [ "$CONFIG_INET" = "y" ]; then
@@ -38,11 +41,9 @@
 tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS
 tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
 tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  tristate 'Amiga FFS filesystem support (EXPERIMENTAL)' CONFIG_AFFS_FS
-  if [ "$CONFIG_AFFS_FS" != "n" ]; then
-    define_bool CONFIG_AMIGA_PARTITION y
-  fi
+tristate 'Amiga FFS filesystem support' CONFIG_AFFS_FS
+if [ "$CONFIG_AFFS_FS" != "n" ]; then
+  define_bool CONFIG_AMIGA_PARTITION y
 fi
 tristate 'UFS filesystem support (read only)' CONFIG_UFS_FS
 if [ "$CONFIG_UFS_FS" != "n" ]; then
diff -urN pc-2.0.21/fs/affs/bitmap.c linux-2.0.21/fs/affs/bitmap.c
--- pc-2.0.21/fs/affs/bitmap.c	Sun May 19 14:22:19 1996
+++ linux-2.0.21/fs/affs/bitmap.c	Wed Sep 18 19:44:29 1996
@@ -149,7 +149,7 @@
 	/* prealloc as much as possible within this word, but not in header zone */
 
 	if (zone_no) {
-		while (inode->u.affs_i.i_pa_cnt < MAX_PREALLOC && ++fb < 32) {
+		while (inode->u.affs_i.i_pa_cnt < AFFS_MAX_PREALLOC && ++fb < 32) {
 			fb = find_next_zero_bit(&w,32,fb);
 			if (fb > 31)
 				break;
@@ -158,7 +158,7 @@
 				break;
 			}
 			inode->u.affs_i.i_data[inode->u.affs_i.i_pa_last++] = fwb + fb;
-			inode->u.affs_i.i_pa_last &= MAX_PREALLOC - 1;
+			inode->u.affs_i.i_pa_last &= AFFS_MAX_PREALLOC - 1;
 			inode->u.affs_i.i_pa_cnt++;
 			az->az_free--;
 		}
@@ -309,7 +309,7 @@
 		inode->u.affs_i.i_pa_cnt--;
 		unlock_super(sb);
 		block = inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++];
-		inode->u.affs_i.i_pa_next &= MAX_PREALLOC - 1;
+		inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
 		goto init_block;
 	}
 	unlock_super(sb);
diff -urN pc-2.0.21/fs/affs/file.c linux-2.0.21/fs/affs/file.c
--- pc-2.0.21/fs/affs/file.c	Sun May 19 14:22:19 1996
+++ linux-2.0.21/fs/affs/file.c	Wed Sep 18 19:44:30 1996
@@ -19,6 +19,7 @@
 #include <linux/fcntl.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
+#include <linux/malloc.h>
 #include <linux/stat.h>
 #include <linux/locks.h>
 #include <linux/dirent.h>
@@ -30,9 +31,14 @@
 #define MIN(a,b) (((a)<(b))?(a):(b))
 #define MAX(a,b) (((a)>(b))?(a):(b))
 
+#if PAGE_SIZE < 4096
+#error PAGE_SIZE must be at least 4096
+#endif
+
 static int affs_file_read_ofs(struct inode *inode, struct file *filp, char *buf, int count);
 static int affs_file_write(struct inode *inode, struct file *filp, const char *buf, int count);
 static int affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf, int count);
+static int affs_open_file(struct inode *inode, struct file *filp);
 static void affs_release_file(struct inode *inode, struct file *filp);
 
 static struct file_operations affs_file_operations = {
@@ -43,7 +49,7 @@
 	NULL,			/* select - default */
 	NULL,			/* ioctl - default */
 	generic_file_mmap,	/* mmap */
-	NULL,			/* no special open is needed */
+	affs_open_file,		/* special open is needed */
 	affs_release_file,	/* release */
 	file_fsync		/* brute force, but works */
 };
@@ -77,7 +83,7 @@
 	NULL,			/* select - default */
 	NULL,			/* ioctl - default */
 	NULL,			/* mmap */
-	NULL,			/* no special open is needed */
+	affs_open_file,		/* special open is needed */
 	affs_release_file,	/* release */
 	file_fsync		/* brute force, but works */
 };
@@ -103,12 +109,133 @@
 	NULL			/* smap */
 };
 
+#define AFFS_ISINDEX(x)	((x < 129) ||				\
+			 (x < 512 && (x & 1) == 0) ||		\
+			 (x < 1024 && (x & 3) == 0) ||		\
+			 (x < 2048 && (x & 15) == 0) ||		\
+			 (x < 4096 && (x & 63) == 0) ||		\
+			 (x < 20480 && (x & 255) == 0) ||	\
+			 (x < 36864 && (x & 511) == 0))
+
+/* The keys of the extension blocks are stored in a 512-entry
+ * deep cache. In order to save memory, not every key of later
+ * extension blocks is stored - the larger the file gets, the
+ * bigger the holes inbetween.
+ */
+
+static int
+seqnum_to_index(int seqnum)
+{
+	/* All of the first 127 keys are stored */
+	if (seqnum < 128)
+		return seqnum;
+	seqnum -= 128;
+
+	/* Of the next 384 keys, every 2nd is kept */
+	if (seqnum < (192 * 2))
+		return 128 + (seqnum >> 1);
+	seqnum -= 192 * 2;
+	
+	/* Every 4th of the next 512 */
+	if (seqnum < (128 * 4))
+		return 128 + 192 + (seqnum >> 2);
+	seqnum -= 128 * 4;
+
+	/* Every 16th of the next 1024 */
+	if (seqnum < (64 * 16))
+		return 128 + 192 + 128 + (seqnum >> 4);
+	seqnum -= 64 * 16;
+
+	/* Every 64th of the next 2048 */
+	if (seqnum < (32 * 64))
+		return 128 + 192 + 128 + 64 + (seqnum >> 6);
+	seqnum -= 32 * 64;
+
+	/* Every 256th of the next 16384 */
+	if (seqnum < (64 * 256))
+		return 128 + 192 + 128 + 64 + 32 + (seqnum >> 8);
+	seqnum -= 64 * 256;
+
+	/* Every 512th upto 36479 (1.3 GB with 512 byte blocks).
+	 * Seeking to positions behind this will get slower
+	 * than dead snails nailed to the ground. But if
+	 * someone uses files that large with 512-byte blocks,
+	 * he or she deserves no better.
+	 */
+	
+	if (seqnum > (31 * 512))
+		seqnum = 31 * 512;
+	return 128 + 192 + 128 + 64 + 32 + 64 + (seqnum >> 9);
+}
+
+/* Now the other way round: Calculate the sequence
+ * number of a extension block of a key at the
+ * given index in the cache.
+ */
+
+static int
+index_to_seqnum(int index)
+{
+	if (index < 128)
+		return index;
+	index -= 128;
+	if (index < 192)
+		return 128 + (index << 1);
+	index -= 192;
+	if (index < 128)
+		return 128 + 192 * 2 + (index << 2);
+	index -= 128;
+	if (index < 64)
+		return 128 + 192 * 2 + 128 * 4 + (index << 4);
+	index -= 64;
+	if (index < 32)
+		return 128 + 192 * 2 + 128 * 4 + 64 * 16 + (index << 6);
+	index -= 32;
+	if (index < 64)
+		return 128 + 192 * 2 + 128 * 4 + 64 * 16 + 32 * 64 + (index << 8);
+	index -= 64;
+	return 128 + 192 * 2 + 128 * 4 + 64 * 16 + 32 * 64 + 64 * 256 + (index << 9);
+}
+
+static int __inline__
+calc_key(struct inode *inode, int *ext)
+{
+	int		  index;
+	struct key_cache *kc;
+
+	for (index = 0; index < 4; index++) {
+		kc = &inode->u.affs_i.i_ec->kc[index];
+		if (*ext == kc->kc_this_seq) {
+			return kc->kc_this_key;
+		} else if (*ext == kc->kc_this_seq + 1) {
+			if (kc->kc_next_key)
+				return kc->kc_next_key;
+			else {
+				(*ext)--;
+				return kc->kc_this_key;
+			}
+		}
+	}
+	index = seqnum_to_index(*ext);
+	if (index > inode->u.affs_i.i_ec->max_ext)
+		index = inode->u.affs_i.i_ec->max_ext;
+	*ext = index_to_seqnum(index);
+	return inode->u.affs_i.i_ec->ec[index];
+}
+
 int
 affs_bmap(struct inode *inode, int block)
 {
 	struct buffer_head	*bh;
-	int			 ext, key;
+	int			 ext, key, nkey;
 	int			 ptype, stype;
+	int			 index;
+	int			 keycount;
+	struct key_cache	*kc;
+	struct key_cache	*tkc;
+	struct timeval		 tv;
+	__s32			*keyp;
+	int			 i;
 
 	pr_debug("AFFS: bmap(%lu,%d)\n",inode->i_ino,block);
 
@@ -116,246 +243,197 @@
 		printk("affs_bmap: block < 0\n");
 		return 0;
 	}
-
-	/* If this is a hard link, quietly exchange the inode with the original */
-
-	key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
-
-	ext = block / AFFS_I2HSIZE(inode);
-	if (ext) {
-		if (ext > inode->u.affs_i.i_max_ext)
-			ext = inode->u.affs_i.i_max_ext;
-		if (ext)
-			key = inode->u.affs_i.i_ext[ext -  1];
-		block -= ext * AFFS_I2HSIZE(inode);
+	if (!inode->u.affs_i.i_ec) {
+		printk("affs_bmap(): No ext_cache!?\n");
+		return 0;
 	}
 
+	/* Try to find the requested key in the cache.
+	 * In order to speed this up as much as possible,
+	 * the cache line lookup is done in a seperate
+	 * step.
+	 */
+
+	for (i = 0; i < 4; i++) {
+		tkc = &inode->u.affs_i.i_ec->kc[i];
+		/* Look in any cache if the key is there */
+		if (block <= tkc->kc_last && block >= tkc->kc_first) {
+			return tkc->kc_keys[block - tkc->kc_first];
+		}
+	}
+	kc = NULL;
+	tv = xtime;
+	for (i = 0; i < 4; i++) {
+		tkc = &inode->u.affs_i.i_ec->kc[i];
+		if (tkc->kc_lru_time.tv_sec > tv.tv_sec)
+			continue;
+		if (tkc->kc_lru_time.tv_sec < tv.tv_sec ||
+		    tkc->kc_lru_time.tv_usec < tv.tv_usec) {
+			kc = tkc;
+			tv = tkc->kc_lru_time;
+		}
+	}
+	if (!kc)	/* Really shouldn't happen */
+		kc = tkc;
+	kc->kc_lru_time = xtime;
+	keyp            = kc->kc_keys;
+	kc->kc_first    = block;
+	kc->kc_last     = -1;
+	keycount        = AFFS_KCSIZE;
+
+	/* Calculate sequence number of the extension block where the
+	 * number of the requested block is stored. 0 means it's in
+	 * the file header.
+	 */
+
+	ext    = block / AFFS_I2HSIZE(inode);
+	key    = calc_key(inode,&ext);
+	block -= ext * AFFS_I2HSIZE(inode);
+
 	for (;;) {
 		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
 		if (!bh)
 			return 0;
-		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) ||
-		    (ptype != T_SHORT && ptype != T_LIST) || stype != ST_FILE) {
+		index = seqnum_to_index(ext);
+		if (index > inode->u.affs_i.i_ec->max_ext &&
+		    (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) ||
+		     (ptype != T_SHORT && ptype != T_LIST) || stype != ST_FILE)) {
 			affs_brelse(bh);
 			return 0;
 		}
-		if (block < AFFS_I2HSIZE(inode))
-			break;
-		block -= AFFS_I2HSIZE(inode);
-		key    = htonl(FILE_END(bh->b_data,inode)->extension);
-		affs_brelse(bh);
-		if (ext < EXT_CACHE_SIZE - 1) {
-			inode->u.affs_i.i_ext[ext] = key;
-			inode->u.affs_i.i_max_ext  = ++ext;
-		}
-	}
-	key = AFFS_GET_HASHENTRY(bh->b_data,(AFFS_I2HSIZE(inode) - 1) - block);
-	affs_brelse(bh);
-	return key;
-}
-
-struct buffer_head *
-affs_getblock(struct inode *inode, int block)
-{
-	struct buffer_head	*bh;
-	struct buffer_head	*ebh;
-	int			 key;
-	int			 ext;
-	int			 cnt, j, pt;
-
-	pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block);
-
-	if (block < 0)
-		return NULL;
-	key = inode->i_ino;
-	pt  = T_SHORT;
-
-	ext = block / AFFS_I2HSIZE(inode);
-	if (ext) {
-		if (ext > inode->u.affs_i.i_max_ext)
-			ext = inode->u.affs_i.i_max_ext;
-		if (ext) {
-			key    = inode->u.affs_i.i_ext[ext - 1];
-			block -= ext * AFFS_I2HSIZE(inode);
-			pt     = T_LIST;
-		}
-	}
-
-	for (;;) {
-		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
-		if (!bh)
-			return NULL;
-		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cnt,&j) ||
-		    cnt != pt || j != ST_FILE) {
-		    	printk("AFFS: getblock(): inode %d is not a valid %s\n",key,
-			       pt == T_SHORT ? "file header" : "extension block");
-			affs_brelse(bh);
-			return NULL;
-		}
-		j = htonl(((struct file_front *)bh->b_data)->block_count);
-		while (j < AFFS_I2HSIZE(inode) && j <= block) {
-			key = affs_new_data(inode);
-			if (!key)
-				break;
-			lock_super(inode->i_sb);
-			if (AFFS_BLOCK(bh->b_data,inode,j)) {
-				unlock_super(inode->i_sb);
-				printk("AFFS: getblock(): block already allocated\n");
-				affs_free_block(inode->i_sb,key);
-				j++;
-				continue;
+		nkey = htonl(FILE_END(bh->b_data,inode)->extension);
+		if (block < AFFS_I2HSIZE(inode)) {
+			/* Fill cache as much as possible */
+			if (keycount) {
+				kc->kc_first = ext * AFFS_I2HSIZE(inode) + block;
+				keycount     = keycount < AFFS_I2HSIZE(inode) - block ? keycount :
+						AFFS_I2HSIZE(inode) - block;
+				for (i = 0; i < keycount; i++)
+					kc->kc_keys[i] = htonl(AFFS_BLOCK(bh->b_data,inode,block + i));
+				kc->kc_last = kc->kc_first + i - 1;
 			}
-			unlock_super(inode->i_sb);
-			AFFS_BLOCK(bh->b_data,inode,j) = ntohl(key);
-			j++;
-		}
-		if (pt == T_SHORT)
-			((struct file_front *)bh->b_data)->first_data =
-								AFFS_BLOCK(bh->b_data,inode,0);
-		((struct file_front *)bh->b_data)->block_count = ntohl(j);
-		affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
-		mark_buffer_dirty(bh,1);
-
-		if (block < j)
 			break;
-		if (j < AFFS_I2HSIZE(inode)) {
-			affs_brelse(bh);
-			return NULL;
 		}
-
 		block -= AFFS_I2HSIZE(inode);
-		key    = htonl(FILE_END(bh->b_data,inode)->extension);
-		if (!key) {
-			key = affs_new_header(inode);
-			if (!key) {
-				affs_brelse(bh);
-				return NULL;
-			}
-			ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
-			if (!ebh) {
-				affs_free_block(inode->i_sb,key);
-				return NULL;
-			}
-			((struct file_front *)ebh->b_data)->primary_type = ntohl(T_LIST);
-			((struct file_front *)ebh->b_data)->own_key      = ntohl(key);
-			FILE_END(ebh->b_data,inode)->secondary_type      = ntohl(ST_FILE);
-			FILE_END(ebh->b_data,inode)->parent              = ntohl(inode->i_ino);
-			affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
-			FILE_END(bh->b_data,inode)->extension = ntohl(key);
-			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
-			mark_buffer_dirty(bh,1);
-			affs_brelse(bh);
-			bh = ebh;
-		}
 		affs_brelse(bh);
-		pt = T_LIST;
-		if (ext < EXT_CACHE_SIZE - 1) {
-			inode->u.affs_i.i_ext[ext] = key;
-			inode->u.affs_i.i_max_ext  = ++ext;
-		}
-	}
+		ext++;
+		if (index > inode->u.affs_i.i_ec->max_ext && AFFS_ISINDEX(ext)) {
+			inode->u.affs_i.i_ec->ec[index] = nkey;
+			inode->u.affs_i.i_ec->max_ext   = index;
+		}
+		key = nkey;
+	}
+	kc->kc_this_key = key;
+	kc->kc_this_seq = ext;
+	kc->kc_next_key = nkey;
 	key = htonl(AFFS_BLOCK(bh->b_data,inode,block));
 	affs_brelse(bh);
-	if (!key)
-		return NULL;
-	
-	return affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+	return key;
 }
 
 struct buffer_head *
-affs_getblock_ofs(struct inode *inode, int block, int *blk_key)
+affs_getblock(struct inode *inode, int block)
 {
 	struct buffer_head	*bh;
-	struct buffer_head	*pbh;
 	struct buffer_head	*ebh;
-	int			 key;
+	struct buffer_head	*pbh;
+	struct key_cache	*kc;
+	int			 key, nkey;
 	int			 ext;
-	int			 cnt, j, pt;
+	int			 cf, j, pt;
+	int			 index;
+	int			 ofs;
 
-	pr_debug("AFFS: getblock_ofs(%lu,%d)\n",inode->i_ino,block);
+	pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block);
 
 	if (block < 0)
 		return NULL;
-	key = inode->i_ino;
-	pt  = T_SHORT;
 
-	ext = block / AFFS_I2HSIZE(inode);
-	if (ext) {
-		if (ext > inode->u.affs_i.i_max_ext)
-			ext = inode->u.affs_i.i_max_ext;
-		if (ext) {
-			key    = inode->u.affs_i.i_ext[ext - 1];
-			block -= ext * AFFS_I2HSIZE(inode);
-			pt     = T_LIST;
-		}
-	}
+	/* Writers always use cache line 3. In almost all cases, files
+	 * will be written by only one process at the same time, and
+	 * they also will be written in strict sequential order. Thus
+	 * there is not much sense in looking whether the key of the
+	 * requested block is available - it won't be there.
+	 */
+	kc     = &inode->u.affs_i.i_ec->kc[3];
+	ofs    = inode->i_sb->u.affs_sb.s_flags & SF_OFS;
+	ext    = block / AFFS_I2HSIZE(inode);
+	key    = calc_key(inode,&ext);
+	block -= ext * AFFS_I2HSIZE(inode);
+	pt     = ext ? T_LIST : T_SHORT;
+	pbh    = NULL;
 
-	pbh = NULL;
 	for (;;) {
 		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
 		if (!bh)
 			return NULL;
-		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cnt,&j) ||
-		    cnt != pt || j != ST_FILE) {
+		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j) ||
+		    cf != pt || j != ST_FILE) {
 		    	printk("AFFS: getblock(): inode %d is not a valid %s\n",key,
 			       pt == T_SHORT ? "file header" : "extension block");
 			affs_brelse(bh);
 			return NULL;
 		}
-		j = htonl(((struct file_front *)bh->b_data)->block_count);
+		j  = htonl(((struct file_front *)bh->b_data)->block_count);
+		cf = 0;
 		while (j < AFFS_I2HSIZE(inode) && j <= block) {
-			if (!pbh && inode->u.affs_i.i_lastblock >= 0) {
+			if (ofs && !pbh && inode->u.affs_i.i_lastblock >= 0) {
 				if (j > 0)
 					pbh = affs_bread(inode->i_dev,ntohl(AFFS_BLOCK(bh->b_data,inode,j - 1)),
 							 AFFS_I2BSIZE(inode));
 				else
-					pbh = affs_getblock_ofs(inode,inode->u.affs_i.i_lastblock,&key);
+					pbh = affs_getblock(inode,inode->u.affs_i.i_lastblock);
 				if (!pbh) {
 					printk("AFFS: getblock(): cannot get last block in file\n");
 					break;
 				}
 			}
-			key = affs_new_data(inode);
-			if (!key)
+			nkey = affs_new_data(inode);
+			if (!nkey)
 				break;
 			lock_super(inode->i_sb);
 			if (AFFS_BLOCK(bh->b_data,inode,j)) {
 				unlock_super(inode->i_sb);
 				printk("AFFS: getblock(): block already allocated\n");
-				affs_free_block(inode->i_sb,key);
+				affs_free_block(inode->i_sb,nkey);
 				j++;
 				continue;
 			}
-			AFFS_BLOCK(bh->b_data,inode,j) = ntohl(key);
 			unlock_super(inode->i_sb);
-			ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
-			if (!ebh) {
-				printk("AFFS: getblock(): cannot get block %d\n",key);
-				affs_free_block(inode->i_sb,key);
-				AFFS_BLOCK(bh->b_data,inode,j) = 0;
-				break;
-			}
-			inode->u.affs_i.i_lastblock++;
-			DATA_FRONT(ebh)->primary_type    = ntohl(T_DATA);
-			DATA_FRONT(ebh)->header_key      = ntohl(inode->i_ino);
-			DATA_FRONT(ebh)->sequence_number = ntohl(inode->u.affs_i.i_lastblock + 1);
-			if (pbh) {
-				DATA_FRONT(pbh)->data_size = ntohl(AFFS_I2BSIZE(inode) - 24);
-				DATA_FRONT(pbh)->next_data = ntohl(key);
-				affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5);
-				mark_buffer_dirty(pbh,0);
-				mark_buffer_dirty(ebh,0);
-				affs_brelse(pbh);
+			AFFS_BLOCK(bh->b_data,inode,j) = ntohl(nkey);
+			if (ofs) {
+				ebh = affs_bread(inode->i_dev,nkey,AFFS_I2BSIZE(inode));
+				if (!ebh) {
+					printk("AFFS: getblock(): cannot get block %d\n",nkey);
+					affs_free_block(inode->i_sb,nkey);
+					AFFS_BLOCK(bh->b_data,inode,j) = 0;
+					break;
+				}
+				inode->u.affs_i.i_lastblock++;
+				DATA_FRONT(ebh)->primary_type    = ntohl(T_DATA);
+				DATA_FRONT(ebh)->header_key      = ntohl(inode->i_ino);
+				DATA_FRONT(ebh)->sequence_number = ntohl(inode->u.affs_i.i_lastblock + 1);
+				if (pbh) {
+					DATA_FRONT(pbh)->data_size = ntohl(AFFS_I2BSIZE(inode) - 24);
+					DATA_FRONT(pbh)->next_data = ntohl(nkey);
+					affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5);
+					mark_buffer_dirty(pbh,0);
+					mark_buffer_dirty(ebh,0);
+					affs_brelse(pbh);
+				}
+				pbh = ebh;
 			}
-			pbh = ebh;
 			j++;
+			cf = 1;
 		}
-		if (pt == T_SHORT)
-			((struct file_front *)bh->b_data)->first_data =
+		if (cf) {
+			if (pt == T_SHORT)
+				((struct file_front *)bh->b_data)->first_data =
 								AFFS_BLOCK(bh->b_data,inode,0);
-		((struct file_front *)bh->b_data)->block_count = ntohl(j);
-		affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
-		mark_buffer_dirty(bh,1);
+			((struct file_front *)bh->b_data)->block_count = ntohl(j);
+			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+			mark_buffer_dirty(bh,1);
+		}
 
 		if (block < j) {
 			if (pbh)
@@ -393,17 +471,21 @@
 		}
 		affs_brelse(bh);
 		pt = T_LIST;
-		if (ext < EXT_CACHE_SIZE - 1) {
-			inode->u.affs_i.i_ext[ext] = key;
-			inode->u.affs_i.i_max_ext  = ++ext;
+		ext++;
+		if ((index = seqnum_to_index(ext)) > inode->u.affs_i.i_ec->max_ext &&
+		    AFFS_ISINDEX(ext) && inode->u.affs_i.i_ec) {
+			inode->u.affs_i.i_ec->ec[index] = key;
+			inode->u.affs_i.i_ec->max_ext   = index;
 		}
 	}
+	kc->kc_this_key = key;
+	kc->kc_this_seq = ext;
+	kc->kc_next_key = htonl(FILE_END(bh->b_data,inode)->extension);
 	key = htonl(AFFS_BLOCK(bh->b_data,inode,block));
 	affs_brelse(bh);
 	if (!key)
 		return NULL;
-	*blk_key = key;
-
+	
 	return affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
 }
 
@@ -543,7 +625,6 @@
 	off_t			 pos;
 	int			 written;
 	int			 c;
-	int			 key;
 	int			 blocksize;
 	struct buffer_head	*bh;
 	struct inode		*ino;
@@ -580,7 +661,7 @@
 	blocksize = AFFS_I2BSIZE(inode) - 24;
 	written   = 0;
 	while (written < count) {
-		bh = affs_getblock_ofs(inode,pos / blocksize,&key);
+		bh = affs_getblock(inode,pos / blocksize);
 		if (!bh) {
 			if (!written)
 				written = -ENOSPC;
@@ -654,15 +735,12 @@
 	blocksize = AFFS_I2BSIZE(inode) - ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) ? 24 : 0);
 	first = (inode->i_size + blocksize - 1) / blocksize;
 	if (inode->u.affs_i.i_lastblock < first - 1) {
-		if (inode->i_sb->u.affs_sb.s_flags & SF_OFS)
-			bh = affs_getblock_ofs(inode,first - 1,&ekey);
-		else
 			bh = affs_getblock(inode,first - 1);
 
 		while (inode->u.affs_i.i_pa_cnt) {		/* Free any preallocated blocks */
 			affs_free_block(inode->i_sb,
 					inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]);
-			inode->u.affs_i.i_pa_next &= MAX_PREALLOC - 1;
+			inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
 			inode->u.affs_i.i_pa_cnt--;
 		}
 		if (inode->u.affs_i.i_zone) {
@@ -755,10 +833,53 @@
 		ekey = key;
 	}
 	inode->u.affs_i.i_lastblock = ((inode->i_size + blocksize - 1) / blocksize) - 1;
-	inode->u.affs_i.i_max_ext   = 0;
+
+	/* Invalidate cache */
+	if (inode->u.affs_i.i_ec) {
+		inode->u.affs_i.i_ec->max_ext = 0;
+		for (key = 0; key < 3; key++) {
+			inode->u.affs_i.i_ec->kc[key].kc_next_key = 0;
+			inode->u.affs_i.i_ec->kc[key].kc_last     = -1;
+		}
+	}
+
 	iput(ino);
 }
 
+static int
+affs_open_file(struct inode *inode, struct file *filp)
+{
+	int	 error;
+	int	 key;
+	int	 i;
+
+	pr_debug("AFFS: open_file(ino=%lu)\n",inode->i_ino);
+
+	error = 0;
+	inode->u.affs_i.i_cache_users++;
+	lock_super(inode->i_sb);
+	if (!inode->u.affs_i.i_ec) {
+		inode->u.affs_i.i_ec = (struct ext_cache *)get_free_page(GFP_KERNEL);
+		if (!inode->u.affs_i.i_ec) {
+			printk("AFFS: cache allocation failed\n");
+			error = ENOMEM;
+		} else {
+			/* We only have to initialize non-zero values.
+			 * get_free_page() zeroed the page already.
+			 */
+			key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
+			inode->u.affs_i.i_ec->ec[0] = key;
+			for (i = 0; i < 4; i++) {
+				inode->u.affs_i.i_ec->kc[i].kc_this_key = key;
+				inode->u.affs_i.i_ec->kc[i].kc_last     = -1;
+			}
+		}
+	}
+	unlock_super(inode->i_sb);
+
+	return error;
+}
+
 static void
 affs_release_file(struct inode *inode, struct file *filp)
 {
@@ -770,7 +891,7 @@
 		while (inode->u.affs_i.i_pa_cnt) {
 			affs_free_block(inode->i_sb,
 					inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]);
-			inode->u.affs_i.i_pa_next &= MAX_PREALLOC - 1;
+			inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
 			inode->u.affs_i.i_pa_cnt--;
 		}
 		if (inode->u.affs_i.i_zone) {
@@ -781,4 +902,22 @@
 			unlock_super(inode->i_sb);
 		}
 	}
+	lock_super(inode->i_sb);
+	if (--inode->u.affs_i.i_cache_users == 0) {
+		if (inode->u.affs_i.i_ec) {
+#if 0
+			{
+				int i, *p = inode->u.affs_i.i_ec->ec;
+				for (i = 0; i < 640; i += 16) {
+					printk("# %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d\n",
+					  p[0+i],p[1+i],p[2+i],p[3+i],p[4+i],p[5+i],p[6+i],p[7+i],
+					  p[8+i],p[9+i],p[10+i],p[11+i],p[12+i],p[13+i],p[14+i],p[15+i]);
+				}
+			}
+#endif
+			free_page((unsigned long)inode->u.affs_i.i_ec);
+			inode->u.affs_i.i_ec = NULL;
+		}
+	}
+	unlock_super(inode->i_sb);
 }
diff -urN pc-2.0.21/fs/affs/inode.c linux-2.0.21/fs/affs/inode.c
--- pc-2.0.21/fs/affs/inode.c	Thu Jul 25 08:08:28 1996
+++ linux-2.0.21/fs/affs/inode.c	Wed Sep 18 19:44:31 1996
@@ -154,7 +154,7 @@
 			}
 			*mount_opts |= SF_IMMUTABLE;
 		}
-		else if (!strcmp(this_char,"verbose")) {
+		if (!strcmp(this_char,"verbose")) {
 			if (value) {
 				printk("AFFS: option verbose does not take an argument\n");
 				return 0;
@@ -333,35 +333,53 @@
 
 	s->u.affs_sb.s_hashsize = 0;
 	if (blocksize > 0) {
-		chksum = blocksize;
-		num_bm = blocksize;
+		i = blocksize;
+		j = blocksize;
 	} else {
-		chksum = 512;
-		num_bm = 4096;
+		i = 512;
+		j = 4096;
 	}
-	for (blocksize = chksum; blocksize <= num_bm; blocksize <<= 1, size >>= 1) {
+	for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) {
 		if (root_block < 0)
 			s->u.affs_sb.s_root_block = (reserved + size - 1) / 2;
 		else
 			s->u.affs_sb.s_root_block = root_block;
-		pr_debug("Trying bs=%d bytes, root at %d, size=%d blocks (%d reserved)\n",
-			 blocksize,s->u.affs_sb.s_root_block,size,reserved);
 		set_blocksize(dev,blocksize);
-		bh = affs_bread(dev,s->u.affs_sb.s_root_block,blocksize);
-		if (!bh) {
-			printk("AFFS: unable to read root block\n");
-			goto out;
-		}
-		if (!affs_checksum_block(blocksize,bh->b_data,&ptype,&stype) &&
-		    ptype == T_SHORT && stype == ST_ROOT) {
-			s->s_blocksize          = blocksize;
-			s->u.affs_sb.s_hashsize = blocksize / 4 - 56;
-			break;
+
+		/* The root block location that was calculated above is not
+		 * correct if the partition size is an odd number of 512-
+		 * byte blocks, which will be rounded down to a number of
+		 * 1024-byte blocks, and if there were an even number of
+		 * reserved blocks. Ideally, all partition checkers should
+		 * report the real number of blocks of the real blocksize,
+		 * but since this just cannot be done, we have to try to
+		 * find the root block anyways. In the above case, it is one
+		 * block behind the calculated one. So we check this one, too.
+		 */
+		for (num_bm = 0; num_bm < 2; num_bm++) {
+			pr_debug("AFFS: Dev %s - trying bs=%d bytes, root at %d, "
+				 "size=%d blocks, %d reserved\n",kdevname(dev),blocksize,
+				 s->u.affs_sb.s_root_block + num_bm,size,reserved);
+			bh = affs_bread(dev,s->u.affs_sb.s_root_block + num_bm,blocksize);
+			if (!bh) {
+				printk("AFFS: unable to read root block\n");
+				goto out;
+			}
+			if (!affs_checksum_block(blocksize,bh->b_data,&ptype,&stype) &&
+			    ptype == T_SHORT && stype == ST_ROOT) {
+				s->s_blocksize             = blocksize;
+				s->u.affs_sb.s_hashsize    = blocksize / 4 - 56;
+				s->u.affs_sb.s_root_block += num_bm;
+				key                        = 1;
+				break;
+			}
 		}
+		if (key)
+			break;
 		affs_brelse(bh);
 		bh = NULL;
 	}
-	if (!s->u.affs_sb.s_hashsize) {
+	if (!key) {
 		affs_brelse(bh);
 		if (!silent)
 			printk("AFFS: Can't find a valid root block on device %s\n",kdevname(dev));
@@ -378,16 +396,6 @@
 	bb = affs_bread(dev,0,s->s_blocksize);
 	if (bb) {
 		chksum = htonl(*(__u32 *)bb->b_data);
-
-		/* Dircache filesystems are compatible with non-dircache ones
-		 * when reading. As long as they aren't supported, writing is
-		 * not recommended.
-		 */
-		if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
-		     || chksum == MUFS_DCOFS) && !(s->s_flags & MS_RDONLY)) {
-			printk("AFFS: Dircache FS - mounting %s read only.\n",kdevname(dev));
-			s->s_flags |= MS_RDONLY;
-		}
 		switch (chksum) {
 			case MUFS_FS:
 			case MUFS_INTLFFS:
@@ -410,9 +418,21 @@
 				s->u.affs_sb.s_flags |= SF_OFS;
 				break;
 			case MUFS_DCOFS:
+				if (!(s->s_flags & MS_RDONLY)) {
+					printk("AFFS: Dircache FS - mounting %s read only.\n",
+					       kdevname(dev));
+				}
+				/* fall thru */
 			case MUFS_INTLOFS:
 				s->u.affs_sb.s_flags |= SF_MUFS;
+				if (0)
+				/* fall thru */
 			case FS_DCOFS:
+				if (!(s->s_flags & MS_RDONLY)) {
+					printk("AFFS: Dircache FS - mounting %s read only.\n",
+					       kdevname(dev));
+				}
+				/* fall thru */
 			case FS_INTLOFS:
 				s->u.affs_sb.s_flags |= SF_INTL | SF_OFS;
 				break;
@@ -629,6 +649,7 @@
 	unsigned short		 id;
 
 	pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino);
+
 	lbh   = NULL;
 	block = inode->i_ino;
 	if (!(bh = affs_bread(inode->i_dev,block,AFFS_I2BSIZE(inode)))) {
@@ -646,19 +667,19 @@
 	file_end   = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode));
 	prot       = (htonl(file_end->protect) & ~0x10) ^ FIBF_OWNER;
 
-	inode->u.affs_i.i_protect   = prot;
-	inode->u.affs_i.i_parent    = htonl(file_end->parent);
-	inode->u.affs_i.i_original  = 0;	
-	inode->u.affs_i.i_zone      = 0;
-	inode->u.affs_i.i_hlink     = 0;
-	inode->u.affs_i.i_pa_cnt    = 0;
-	inode->u.affs_i.i_pa_next   = 0;
-	inode->u.affs_i.i_pa_last   = 0;
-	inode->u.affs_i.i_ext[0]    = 0;
-	inode->u.affs_i.i_max_ext   = 0;
-	inode->u.affs_i.i_lastblock = -1;
-	inode->i_nlink              = 1;
-	inode->i_mode               = 0;
+	inode->u.affs_i.i_protect      = prot;
+	inode->u.affs_i.i_parent       = htonl(file_end->parent);
+	inode->u.affs_i.i_original     = 0;	
+	inode->u.affs_i.i_zone         = 0;
+	inode->u.affs_i.i_hlink        = 0;
+	inode->u.affs_i.i_pa_cnt       = 0;
+	inode->u.affs_i.i_pa_next      = 0;
+	inode->u.affs_i.i_pa_last      = 0;
+	inode->u.affs_i.i_ec           = NULL;
+	inode->u.affs_i.i_cache_users  = 0;
+	inode->u.affs_i.i_lastblock    = -1;
+	inode->i_nlink                 = 1;
+	inode->i_mode                  = 0;
 
 	if (inode->i_sb->u.affs_sb.s_flags & SF_SETMODE)
 		inode->i_mode = inode->i_sb->u.affs_sb.s_mode;
@@ -791,7 +812,7 @@
 					gid = inode->i_gid ^ ~0;
 			}
 			if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETUID))
-				file_end->owner_uid = ntohs(uid);
+				file_end->owner_gid = ntohs(uid);
 			if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETGID))
 				file_end->owner_gid = ntohs(gid);
 		}
@@ -876,16 +897,16 @@
 	inode->i_blksize = 0;
 	inode->i_mtime   = inode->i_atime = inode->i_ctime = CURRENT_TIME;
 
-	inode->u.affs_i.i_original  = 0;
-	inode->u.affs_i.i_parent    = dir->i_ino;
-	inode->u.affs_i.i_zone      = 0;
-	inode->u.affs_i.i_hlink     = 0;
-	inode->u.affs_i.i_pa_cnt    = 0;
-	inode->u.affs_i.i_pa_next   = 0;
-	inode->u.affs_i.i_pa_last   = 0;
-	inode->u.affs_i.i_ext[0]    = 0;
-	inode->u.affs_i.i_max_ext   = 0;
-	inode->u.affs_i.i_lastblock = -1;
+	inode->u.affs_i.i_original     = 0;
+	inode->u.affs_i.i_parent       = dir->i_ino;
+	inode->u.affs_i.i_zone         = 0;
+	inode->u.affs_i.i_hlink        = 0;
+	inode->u.affs_i.i_pa_cnt       = 0;
+	inode->u.affs_i.i_pa_next      = 0;
+	inode->u.affs_i.i_pa_last      = 0;
+	inode->u.affs_i.i_ec           = NULL;
+	inode->u.affs_i.i_cache_users  = 0;
+	inode->u.affs_i.i_lastblock    = -1;
 
 	insert_inode_hash(inode);
 
diff -urN pc-2.0.21/fs/binfmt_aout.c linux-2.0.21/fs/binfmt_aout.c
--- pc-2.0.21/fs/binfmt_aout.c	Mon Aug 12 10:10:09 1996
+++ linux-2.0.21/fs/binfmt_aout.c	Sat Aug 24 14:40:39 1996
@@ -327,6 +327,15 @@
 			MAP_FIXED|MAP_PRIVATE, 0);
 		read_exec(bprm->inode, 32, (char *) 0, ex.a_text+ex.a_data, 0);
 #endif
+#if 1
+		/* there's no nice way of flushing a number of
+		   user pages to ram 8*( */
+		flush_cache_all();
+#if 0
+		flush_cache_range(current->mm, (unsigned long) 0,
+				  (unsigned long) 0 + ex.a_text+ex.a_data);
+#endif
+#endif
 	} else {
 		if (ex.a_text & 0xfff || ex.a_data & 0xfff)
 			printk(KERN_NOTICE "executable not page aligned\n");
@@ -343,6 +352,16 @@
 				MAP_FIXED|MAP_PRIVATE, 0);
 			read_exec(bprm->inode, fd_offset,
 				  (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0);
+#if 1
+			/* there's no nice way of flushing a number of
+			   user pages to ram 8*( */
+			flush_cache_all();
+#if 0
+			flush_cache_range(current->mm,
+					  (unsigned long) N_TXTADDR(ex),
+					  (unsigned long) ex.a_text+ex.a_data);
+#endif
+#endif
 			goto beyond_if;
 		}
 
diff -urN pc-2.0.21/fs/buffer.c linux-2.0.21/fs/buffer.c
--- pc-2.0.21/fs/buffer.c	Fri Sep 20 11:52:39 1996
+++ linux-2.0.21/fs/buffer.c	Mon Sep 23 22:31:51 1996
@@ -34,6 +34,7 @@
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <asm/io.h>
+#include <asm/bitops.h>
 
 #define NR_SIZES 5
 static char buffersize_index[17] =
@@ -1849,6 +1850,32 @@
 		 return try_to_generate_cluster(dev, b[0], size);
 	else
 		 return reassign_cluster(dev, b[0], size);
+}
+
+unsigned long generate_cluster_swab(kdev_t dev, int b[], int size)
+{
+	int i, offset;
+	
+	for (i = 0, offset = 0 ; offset < PAGE_SIZE ; i++, offset += size) {
+		if (i && swab32(b[i])-1 != swab32(b[i-1]))
+			return 0;  /* No need to cluster */
+		if (find_buffer(dev, swab32(b[i]), size)) return 0;
+	}
+
+	/* OK, we have a candidate for a new cluster */
+	
+	/* See if one size of buffer is over-represented in the buffer cache,
+	   if so reduce the numbers of buffers */
+	if (maybe_shrink_lav_buffers(size)) {
+		 int retval;
+		 retval = try_to_generate_cluster(dev, swab32(b[0]), size);
+		 if(retval) return retval;
+	}
+	
+	if (nr_free_pages > min_free_pages*2) 
+		 return try_to_generate_cluster(dev, swab32(b[0]), size);
+	else
+		 return reassign_cluster(dev, swab32(b[0]), size);
 }
 
 
diff -urN pc-2.0.21/fs/ext2/balloc.c linux-2.0.21/fs/ext2/balloc.c
--- pc-2.0.21/fs/ext2/balloc.c	Thu Jan  4 13:14:20 1996
+++ linux-2.0.21/fs/ext2/balloc.c	Thu Aug 29 23:15:43 1996
@@ -69,14 +69,17 @@
 {
 	struct ext2_group_desc * gdp;
 	struct buffer_head * bh;
+	int bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 	
 	gdp = get_group_desc (sb, block_group, NULL);
-	bh = bread (sb->s_dev, gdp->bg_block_bitmap, sb->s_blocksize);
+	bh = bread (sb->s_dev, e_swab (bs, gdp->bg_block_bitmap),
+		    sb->s_blocksize);
 	if (!bh)
 		ext2_panic (sb, "read_block_bitmap",
 			    "Cannot read block bitmap - "
 			    "block_group = %d, block_bitmap = %lu",
-			    block_group, (unsigned long) gdp->bg_block_bitmap);
+			    block_group,
+			    (unsigned long) e_swab (bs, gdp->bg_block_bitmap));
 	sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group;
 	sb->u.ext2_sb.s_block_bitmap[bitmap_nr] = bh;
 }
@@ -174,6 +177,7 @@
 	unsigned long bit;
 	unsigned long i;
 	int bitmap_nr;
+	int bs;
 	struct super_block * sb;
 	struct ext2_group_desc * gdp;
 	struct ext2_super_block * es;
@@ -184,9 +188,10 @@
 		return;
 	}
 	lock_super (sb);
+	bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 	es = sb->u.ext2_sb.s_es;
-	if (block < es->s_first_data_block || 
-	    (block + count) > es->s_blocks_count) {
+	if (block < e_swab (bs, es->s_first_data_block) || 
+	    (block + count) > e_swab (bs, es->s_blocks_count)) {
 		ext2_error (sb, "ext2_free_blocks",
 			    "Freeing blocks not in datazone - "
 			    "block = %lu, count = %lu", block, count);
@@ -196,9 +201,10 @@
 
 	ext2_debug ("freeing block %lu\n", block);
 
-	block_group = (block - es->s_first_data_block) /
+	block_group = (block - e_swab (bs, es->s_first_data_block)) /
 		      EXT2_BLOCKS_PER_GROUP(sb);
-	bit = (block - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb);
+	bit = (block - e_swab (bs, es->s_first_data_block)) %
+	      EXT2_BLOCKS_PER_GROUP(sb);
 	if (bit + count > EXT2_BLOCKS_PER_GROUP(sb))
 		ext2_panic (sb, "ext2_free_blocks",
 			    "Freeing blocks across group boundary - "
@@ -209,11 +215,11 @@
 	gdp = get_group_desc (sb, block_group, &bh2);
 
 	if (test_opt (sb, CHECK_STRICT) &&
-	    (in_range (gdp->bg_block_bitmap, block, count) ||
-	     in_range (gdp->bg_inode_bitmap, block, count) ||
-	     in_range (block, gdp->bg_inode_table,
+	    (in_range (e_swab (bs, gdp->bg_block_bitmap), block, count) ||
+	     in_range (e_swab (bs, gdp->bg_inode_bitmap), block, count) ||
+	     in_range (block, e_swab (bs, gdp->bg_inode_table),
 		       sb->u.ext2_sb.s_itb_per_group) ||
-	     in_range (block + count - 1, gdp->bg_inode_table,
+	     in_range (block + count - 1, e_swab (bs, gdp->bg_inode_table),
 		       sb->u.ext2_sb.s_itb_per_group)))
 		ext2_panic (sb, "ext2_free_blocks",
 			    "Freeing blocks in system zones - "
@@ -221,15 +227,17 @@
 			    block, count);
 
 	for (i = 0; i < count; i++) {
-		if (!clear_bit (bit + i, bh->b_data))
+		if (!ext2_clear_bit (bit + i, bh->b_data))
 			ext2_warning (sb, "ext2_free_blocks",
 				      "bit already cleared for block %lu", 
 				      block);
 		else {
 			if (sb->dq_op)
 				sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize));
-			gdp->bg_free_blocks_count++;
-			es->s_free_blocks_count++;
+			e_set_swab (bs, gdp->bg_free_blocks_count,
+				    e_swab (bs, gdp->bg_free_blocks_count) + 1);
+			e_set_swab (bs, es->s_free_blocks_count,
+				    e_swab (bs, es->s_free_blocks_count) + 1);
 		}
 	}
 	
@@ -261,6 +269,7 @@
 	char * p, * r;
 	int i, j, k, tmp;
 	int bitmap_nr;
+	int bs;
 	struct super_block * sb;
 	struct ext2_group_desc * gdp;
 	struct ext2_super_block * es;
@@ -275,8 +284,9 @@
 		return 0;
 	}
 	lock_super (sb);
+	bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 	es = sb->u.ext2_sb.s_es;
-	if (es->s_free_blocks_count <= es->s_r_blocks_count &&
+	if (e_swab (bs, es->s_free_blocks_count) <= e_swab (bs, es->s_r_blocks_count) &&
 	    (!fsuser() && (sb->u.ext2_sb.s_resuid != current->fsuid) &&
 	     (sb->u.ext2_sb.s_resgid == 0 ||
 	      !in_group_p (sb->u.ext2_sb.s_resgid)))) {
@@ -290,12 +300,15 @@
 	/*
 	 * First, test whether the goal block is free.
 	 */
-	if (goal < es->s_first_data_block || goal >= es->s_blocks_count)
-		goal = es->s_first_data_block;
-	i = (goal - es->s_first_data_block) / EXT2_BLOCKS_PER_GROUP(sb);
+	if (goal < e_swab (bs, es->s_first_data_block) ||
+	    goal >= e_swab (bs, es->s_blocks_count))
+		goal = e_swab (bs, es->s_first_data_block);
+	i = (goal - e_swab (bs, es->s_first_data_block)) /
+	    EXT2_BLOCKS_PER_GROUP(sb);
 	gdp = get_group_desc (sb, i, &bh2);
-	if (gdp->bg_free_blocks_count > 0) {
-		j = ((goal - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb));
+	if (e_swab (bs, gdp->bg_free_blocks_count) > 0) {
+		j = ((goal - e_swab (bs, es->s_first_data_block)) %
+		    EXT2_BLOCKS_PER_GROUP(sb));
 #ifdef EXT2FS_DEBUG
 		if (j)
 			goal_attempts++;
@@ -305,7 +318,7 @@
 
 		ext2_debug ("goal is at %d:%d.\n", i, j);
 
-		if (!test_bit(j, bh->b_data)) {
+		if (!ext2_test_bit(j, bh->b_data)) {
 #ifdef EXT2FS_DEBUG
 			goal_hits++;
 			ext2_debug ("goal bit allocated.\n");
@@ -322,7 +335,7 @@
 			 * next 64-bit boundary is simple..
 			 */
 			int end_goal = (j + 63) & ~63;
-			j = find_next_zero_bit(bh->b_data, end_goal, j);
+			j = ext2_find_next_zero_bit(bh->b_data, end_goal, j);
 			if (j < end_goal)
 				goto got_block;
 		}
@@ -345,7 +358,7 @@
 			j = k;
 			goto search_back;
 		}
-		k = find_next_zero_bit ((unsigned long *) bh->b_data, 
+		k = ext2_find_next_zero_bit ((unsigned long *) bh->b_data, 
 					EXT2_BLOCKS_PER_GROUP(sb),
 					j);
 		if (k < EXT2_BLOCKS_PER_GROUP(sb)) {
@@ -365,7 +378,7 @@
 		if (i >= sb->u.ext2_sb.s_groups_count)
 			i = 0;
 		gdp = get_group_desc (sb, i, &bh2);
-		if (gdp->bg_free_blocks_count > 0)
+		if (e_swab (bs, gdp->bg_free_blocks_count) > 0)
 			break;
 	}
 	if (k >= sb->u.ext2_sb.s_groups_count) {
@@ -379,7 +392,7 @@
 	if (j < EXT2_BLOCKS_PER_GROUP(sb))
 		goto search_back;
 	else
-		j = find_first_zero_bit ((unsigned long *) bh->b_data,
+		j = ext2_find_first_zero_bit ((unsigned long *) bh->b_data,
 					 EXT2_BLOCKS_PER_GROUP(sb));
 	if (j >= EXT2_BLOCKS_PER_GROUP(sb)) {
 		ext2_error (sb, "ext2_new_block",
@@ -394,7 +407,7 @@
 	 * bitmap.  Now search backwards up to 7 bits to find the
 	 * start of this group of free blocks.
 	 */
-	for (k = 0; k < 7 && j > 0 && !test_bit (j - 1, bh->b_data); k++, j--);
+	for (k = 0; k < 7 && j > 0 && !ext2_test_bit (j - 1, bh->b_data); k++, j--);
 	
 got_block:
 
@@ -410,17 +423,18 @@
 			return 0;
 		}
 
-	tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) + es->s_first_data_block;
+	tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) +
+	      e_swab (bs, es->s_first_data_block);
 
 	if (test_opt (sb, CHECK_STRICT) &&
-	    (tmp == gdp->bg_block_bitmap ||
-	     tmp == gdp->bg_inode_bitmap ||
-	     in_range (tmp, gdp->bg_inode_table, sb->u.ext2_sb.s_itb_per_group)))
+	    (tmp == e_swab (bs, gdp->bg_block_bitmap) ||
+	     tmp == e_swab (bs, gdp->bg_inode_bitmap) ||
+	     in_range (tmp, e_swab (bs, gdp->bg_inode_table), sb->u.ext2_sb.s_itb_per_group)))
 		ext2_panic (sb, "ext2_new_block",
 			    "Allocating block in system zone - "
 			    "block = %u", tmp);
 
-	if (set_bit (j, bh->b_data)) {
+	if (ext2_set_bit (j, bh->b_data)) {
 		ext2_warning (sb, "ext2_new_block",
 			      "bit already set for block %d", j);
 		if (sb->dq_op)
@@ -442,15 +456,17 @@
 			if (sb->dq_op)
 				if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(1, sb->s_blocksize)))
 					break;
-			if (set_bit (j + k, bh->b_data)) {
+			if (ext2_set_bit (j + k, bh->b_data)) {
 				if (sb->dq_op)
 					sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize));
 				break;
 			}
 			(*prealloc_count)++;
 		}	
-		gdp->bg_free_blocks_count -= *prealloc_count;
-		es->s_free_blocks_count -= *prealloc_count;
+		e_set_swab (bs, gdp->bg_free_blocks_count,
+			    e_swab (bs, gdp->bg_free_blocks_count) - *prealloc_count);
+		e_set_swab (bs, es->s_free_blocks_count,
+			    e_swab (bs, es->s_free_blocks_count) - *prealloc_count);
 		ext2_debug ("Preallocated a further %lu bits.\n",
 			    *prealloc_count);
 	}
@@ -464,7 +480,7 @@
 		wait_on_buffer (bh);
 	}
 
-	if (j >= es->s_blocks_count) {
+	if (j >= e_swab (bs, es->s_blocks_count)) {
 		ext2_error (sb, "ext2_new_block",
 			    "block >= blocks count - "
 			    "block_group = %d, block=%d", i, j);
@@ -484,9 +500,11 @@
 	ext2_debug ("allocating block %d. "
 		    "Goal hits %d of %d.\n", j, goal_hits, goal_attempts);
 
-	gdp->bg_free_blocks_count--;
+	e_set_swab (bs, gdp->bg_free_blocks_count,
+		    e_swab (bs, gdp->bg_free_blocks_count) - 1);
 	mark_buffer_dirty(bh2, 1);
-	es->s_free_blocks_count--;
+	e_set_swab (bs, es->s_free_blocks_count,
+		    e_swab (bs, es->s_free_blocks_count) - 1);
 	mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 	sb->s_dirt = 1;
 	unlock_super (sb);
@@ -501,29 +519,31 @@
 	unsigned long desc_count, bitmap_count, x;
 	int bitmap_nr;
 	struct ext2_group_desc * gdp;
-	int i;
+	int i, bs;
 	
 	lock_super (sb);
+	bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 	es = sb->u.ext2_sb.s_es;
 	desc_count = 0;
 	bitmap_count = 0;
 	gdp = NULL;
 	for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
 		gdp = get_group_desc (sb, i, NULL);
-		desc_count += gdp->bg_free_blocks_count;
+		desc_count += e_swab (bs, gdp->bg_free_blocks_count);
 		bitmap_nr = load_block_bitmap (sb, i);
 		x = ext2_count_free (sb->u.ext2_sb.s_block_bitmap[bitmap_nr],
 				     sb->s_blocksize);
 		printk ("group %d: stored = %d, counted = %lu\n",
-			i, gdp->bg_free_blocks_count, x);
+			i, e_swab (bs, gdp->bg_free_blocks_count), x);
 		bitmap_count += x;
 	}
 	printk("ext2_count_free_blocks: stored = %lu, computed = %lu, %lu\n",
-	       es->s_free_blocks_count, desc_count, bitmap_count);
+	       e_swab (bs, es->s_free_blocks_count), desc_count, bitmap_count);
 	unlock_super (sb);
 	return bitmap_count;
 #else
-	return sb->u.ext2_sb.s_es->s_free_blocks_count;
+	return e_swab (BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped),
+		       sb->u.ext2_sb.s_es->s_free_blocks_count);
 #endif
 }
 
@@ -531,7 +551,8 @@
 				struct super_block * sb,
 				unsigned char * map)
 {
-	return test_bit ((block - sb->u.ext2_sb.s_es->s_first_data_block) %
+	return ext2_test_bit ((block - e_swab (BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped),
+ 	                 sb->u.ext2_sb.s_es->s_first_data_block)) %
 			 EXT2_BLOCKS_PER_GROUP(sb), map);
 }
 
@@ -543,9 +564,10 @@
 	unsigned long desc_blocks;
 	int bitmap_nr;
 	struct ext2_group_desc * gdp;
-	int i, j;
+	int i, j, bs;
 
 	lock_super (sb);
+	bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 	es = sb->u.ext2_sb.s_es;
 	desc_count = 0;
 	bitmap_count = 0;
@@ -553,49 +575,52 @@
 	desc_blocks = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
 		      EXT2_DESC_PER_BLOCK(sb);
 	for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
+		unsigned long bg_inode_table;
 		gdp = get_group_desc (sb, i, NULL);
-		desc_count += gdp->bg_free_blocks_count;
+		desc_count += e_swab (bs, gdp->bg_free_blocks_count);
 		bitmap_nr = load_block_bitmap (sb, i);
 		bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
 
-		if (!test_bit (0, bh->b_data))
+		if (!ext2_test_bit (0, bh->b_data))
 			ext2_error (sb, "ext2_check_blocks_bitmap",
 				    "Superblock in group %d is marked free", i);
 
 		for (j = 0; j < desc_blocks; j++)
-			if (!test_bit (j + 1, bh->b_data))
+			if (!ext2_test_bit (j + 1, bh->b_data))
 				ext2_error (sb, "ext2_check_blocks_bitmap",
 					    "Descriptor block #%d in group "
 					    "%d is marked free", j, i);
 
-		if (!block_in_use (gdp->bg_block_bitmap, sb, bh->b_data))
+		if (!block_in_use (e_swab (bs, gdp->bg_block_bitmap), sb, bh->b_data))
 			ext2_error (sb, "ext2_check_blocks_bitmap",
 				    "Block bitmap for group %d is marked free",
 				    i);
 
-		if (!block_in_use (gdp->bg_inode_bitmap, sb, bh->b_data))
+		if (!block_in_use (e_swab (bs, gdp->bg_inode_bitmap), sb, bh->b_data))
 			ext2_error (sb, "ext2_check_blocks_bitmap",
 				    "Inode bitmap for group %d is marked free",
 				    i);
 
+		bg_inode_table = e_swab (bs, gdp->bg_inode_table);
 		for (j = 0; j < sb->u.ext2_sb.s_itb_per_group; j++)
-			if (!block_in_use (gdp->bg_inode_table + j, sb, bh->b_data))
+			if (!block_in_use (bg_inode_table + j, sb, bh->b_data))
 				ext2_error (sb, "ext2_check_blocks_bitmap",
 					    "Block #%d of the inode table in "
 					    "group %d is marked free", j, i);
 
 		x = ext2_count_free (bh, sb->s_blocksize);
-		if (gdp->bg_free_blocks_count != x)
+		if (e_swab (bs, gdp->bg_free_blocks_count) != x)
 			ext2_error (sb, "ext2_check_blocks_bitmap",
 				    "Wrong free blocks count for group %d, "
 				    "stored = %d, counted = %lu", i,
-				    gdp->bg_free_blocks_count, x);
+				    e_swab (bs, gdp->bg_free_blocks_count), x);
 		bitmap_count += x;
 	}
-	if (es->s_free_blocks_count != bitmap_count)
+	if (e_swab (bs, es->s_free_blocks_count) != bitmap_count)
 		ext2_error (sb, "ext2_check_blocks_bitmap",
 			    "Wrong free blocks count in super block, "
 			    "stored = %lu, counted = %lu",
-			    (unsigned long) es->s_free_blocks_count, bitmap_count);
+			    (unsigned long) e_swab (bs, es->s_free_blocks_count),
+			    bitmap_count);
 	unlock_super (sb);
 }
diff -urN pc-2.0.21/fs/ext2/dir.c linux-2.0.21/fs/ext2/dir.c
--- pc-2.0.21/fs/ext2/dir.c	Fri Dec 22 12:00:19 1995
+++ linux-2.0.21/fs/ext2/dir.c	Fri Aug 30 13:30:12 1996
@@ -73,27 +73,30 @@
 
 int ext2_check_dir_entry (const char * function, struct inode * dir,
 			  struct ext2_dir_entry * de, struct buffer_head * bh,
-			  unsigned long offset)
+			  unsigned long offset, int bs)
 {
 	const char * error_msg = NULL;
+	int rec_len = e_swab (bs, de->rec_len);
 
-	if (de->rec_len < EXT2_DIR_REC_LEN(1))
+	if (rec_len < EXT2_DIR_REC_LEN(1))
 		error_msg = "rec_len is smaller than minimal";
-	else if (de->rec_len % 4 != 0)
+	else if (rec_len % 4 != 0)
 		error_msg = "rec_len % 4 != 0";
-	else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len))
+	else if (rec_len < EXT2_DIR_REC_LEN(e_swab (bs, de->name_len)))
 		error_msg = "rec_len is too small for name_len";
-	else if (dir && ((char *) de - bh->b_data) + de->rec_len >
+	else if (dir && ((char *) de - bh->b_data) + rec_len >
 		 dir->i_sb->s_blocksize)
 		error_msg = "directory entry across blocks";
-	else if (dir && de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
+	else if (dir && e_swab (bs, de->inode) >
+		 e_swab (bs, dir->i_sb->u.ext2_sb.s_es->s_inodes_count))
 		error_msg = "inode out of bounds";
 
 	if (error_msg != NULL)
 		ext2_error (dir->i_sb, function, "bad entry in directory #%lu: %s - "
 			    "offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
-			    dir->i_ino, error_msg, offset, (unsigned long) de->inode,
-			    de->rec_len, de->name_len);
+			    dir->i_ino, error_msg, offset,
+			    (unsigned long) e_swab (bs, de->inode),
+			    rec_len, e_swab (bs, de->name_len));
 	return error_msg == NULL ? 1 : 0;
 }
 
@@ -106,11 +109,12 @@
 	struct buffer_head * bh, * tmp, * bha[16];
 	struct ext2_dir_entry * de;
 	struct super_block * sb;
-	int err;
+	int err, bs;
 
 	if (!inode || !S_ISDIR(inode->i_mode))
 		return -EBADF;
 	sb = inode->i_sb;
+	bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 
 	stored = 0;
 	bh = NULL;
@@ -161,9 +165,9 @@
 				 * least that it is non-zero.  A
 				 * failure will be detected in the
 				 * dirent test below. */
-				if (de->rec_len < EXT2_DIR_REC_LEN(1))
+				if (e_swab (bs, de->rec_len) < EXT2_DIR_REC_LEN(1))
 					break;
-				i += de->rec_len;
+				i += e_swab (bs, de->rec_len);
 			}
 			offset = i;
 			filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
@@ -175,7 +179,7 @@
 		       && offset < sb->s_blocksize) {
 			de = (struct ext2_dir_entry *) (bh->b_data + offset);
 			if (!ext2_check_dir_entry ("ext2_readdir", inode, de,
-						   bh, offset)) {
+						   bh, offset, bs)) {
 				/* On error, skip the f_pos to the
                                    next block. */
 				filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1))
@@ -183,7 +187,7 @@
 				brelse (bh);
 				return stored;
 			}
-			offset += de->rec_len;
+			offset += e_swab (bs, de->rec_len);
 			if (de->inode) {
 				/* We might block in the next section
 				 * if the data destination is
@@ -192,16 +196,21 @@
 				 * not the directory has been modified
 				 * during the copy operation. */
 				unsigned long version;
-				dcache_add(inode, de->name, de->name_len, de->inode);
+				dcache_add(inode, de->name,
+					   e_swab (bs, de->name_len),
+					   e_swab (bs, de->inode));
 				version = inode->i_version;
-				error = filldir(dirent, de->name, de->name_len, filp->f_pos, de->inode);
+				error = filldir(dirent, de->name,
+						e_swab (bs, de->name_len),
+						filp->f_pos,
+						e_swab (bs, de->inode));
 				if (error)
 					break;
 				if (version != inode->i_version)
 					goto revalidate;
 				stored ++;
 			}
-			filp->f_pos += de->rec_len;
+			filp->f_pos += e_swab (bs, de->rec_len);
 		}
 		offset = 0;
 		brelse (bh);
diff -urN pc-2.0.21/fs/ext2/fsync.c linux-2.0.21/fs/ext2/fsync.c
--- pc-2.0.21/fs/ext2/fsync.c	Sun Nov 26 18:23:10 1995
+++ linux-2.0.21/fs/ext2/fsync.c	Fri Aug 30 13:30:13 1996
@@ -27,15 +27,15 @@
 #define blocksize (EXT2_BLOCK_SIZE(inode->i_sb))
 #define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb))
 
-static int sync_block (struct inode * inode, u32 * block, int wait)
+static int sync_block (struct inode * inode, u32 * block, int wait, int bs)
 {
 	struct buffer_head * bh;
-	int tmp;
+	u32 tmp;
 	
-	if (!*block)
-		return 0;
 	tmp = *block;
-	bh = get_hash_table (inode->i_dev, *block, blocksize);
+	if (!tmp)
+		return 0;
+	bh = get_hash_table (inode->i_dev, e_swab (bs, tmp), blocksize);
 	if (!bh)
 		return 0;
 	if (*block != tmp) {
@@ -56,18 +56,19 @@
 }
 
 static int sync_iblock (struct inode * inode, u32 * iblock, 
-			struct buffer_head ** bh, int wait) 
+			struct buffer_head ** bh, int wait, int bs) 
 {
-	int rc, tmp;
+	int rc;
+	u32 tmp;
 	
 	*bh = NULL;
 	tmp = *iblock;
 	if (!tmp)
 		return 0;
-	rc = sync_block (inode, iblock, wait);
+	rc = sync_block (inode, iblock, wait, bs);
 	if (rc)
 		return rc;
-	*bh = bread (inode->i_dev, tmp, blocksize);
+	*bh = bread (inode->i_dev, e_swab (bs, tmp), blocksize);
 	if (tmp != *iblock) {
 		brelse (*bh);
 		*bh = NULL;
@@ -85,7 +86,7 @@
 	int rc, err = 0;
 
 	for (i = 0; i < EXT2_NDIR_BLOCKS; i++) {
-		rc = sync_block (inode, inode->u.ext2_i.i_data + i, wait);
+		rc = sync_block (inode, inode->u.ext2_i.i_data + i, wait, 0);
 		if (rc > 0)
 			break;
 		if (rc)
@@ -94,20 +95,21 @@
 	return err;
 }
 
-static int sync_indirect (struct inode * inode, u32 * iblock, int wait)
+static int sync_indirect (struct inode * inode, u32 * iblock, int wait, int bs1)
 {
 	int i;
 	struct buffer_head * ind_bh;
 	int rc, err = 0;
+	int bs = BYTE_SWAP(inode->i_sb->u.ext2_sb.s_byte_swapped);
 
-	rc = sync_iblock (inode, iblock, &ind_bh, wait);
+	rc = sync_iblock (inode, iblock, &ind_bh, wait, bs1);
 	if (rc || !ind_bh)
 		return rc;
 	
 	for (i = 0; i < addr_per_block; i++) {
 		rc = sync_block (inode, 
 				 ((u32 *) ind_bh->b_data) + i,
-				 wait);
+				 wait, bs);
 		if (rc > 0)
 			break;
 		if (rc)
@@ -117,20 +119,21 @@
 	return err;
 }
 
-static int sync_dindirect (struct inode * inode, u32 * diblock, int wait)
+static int sync_dindirect (struct inode * inode, u32 * diblock, int wait, int bs1)
 {
 	int i;
 	struct buffer_head * dind_bh;
 	int rc, err = 0;
+	int bs = BYTE_SWAP(inode->i_sb->u.ext2_sb.s_byte_swapped);
 
-	rc = sync_iblock (inode, diblock, &dind_bh, wait);
+	rc = sync_iblock (inode, diblock, &dind_bh, wait, bs1);
 	if (rc || !dind_bh)
 		return rc;
 	
 	for (i = 0; i < addr_per_block; i++) {
 		rc = sync_indirect (inode,
 				    ((u32 *) dind_bh->b_data) + i,
-				    wait);
+				    wait, bs);
 		if (rc > 0)
 			break;
 		if (rc)
@@ -140,20 +143,21 @@
 	return err;
 }
 
-static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait)
+static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait, int bs1)
 {
 	int i;
 	struct buffer_head * tind_bh;
 	int rc, err = 0;
+	int bs = BYTE_SWAP(inode->i_sb->u.ext2_sb.s_byte_swapped);
 
-	rc = sync_iblock (inode, tiblock, &tind_bh, wait);
+	rc = sync_iblock (inode, tiblock, &tind_bh, wait, bs1);
 	if (rc || !tind_bh)
 		return rc;
 	
 	for (i = 0; i < addr_per_block; i++) {
 		rc = sync_dindirect (inode,
 				     ((u32 *) tind_bh->b_data) + i,
-				     wait);
+				     wait, bs);
 		if (rc > 0)
 			break;
 		if (rc)
@@ -181,13 +185,13 @@
 		err |= sync_direct (inode, wait);
 		err |= sync_indirect (inode,
 				      inode->u.ext2_i.i_data+EXT2_IND_BLOCK,
-				      wait);
+				      wait, 0);
 		err |= sync_dindirect (inode,
 				       inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, 
-				       wait);
+				       wait, 0);
 		err |= sync_tindirect (inode, 
 				       inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, 
-				       wait);
+				       wait, 0);
 	}
 skip:
 	err |= ext2_sync_inode (inode);
diff -urN pc-2.0.21/fs/ext2/ialloc.c linux-2.0.21/fs/ext2/ialloc.c
--- pc-2.0.21/fs/ext2/ialloc.c	Sat May  4 09:06:18 1996
+++ linux-2.0.21/fs/ext2/ialloc.c	Fri Aug 30 13:30:14 1996
@@ -68,14 +68,17 @@
 {
 	struct ext2_group_desc * gdp;
 	struct buffer_head * bh;
+	int bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 
 	gdp = get_group_desc (sb, block_group, NULL);
-	bh = bread (sb->s_dev, gdp->bg_inode_bitmap, sb->s_blocksize);
+	bh = bread (sb->s_dev, e_swab (bs, gdp->bg_inode_bitmap),
+		    sb->s_blocksize);
 	if (!bh)
 		ext2_panic (sb, "read_inode_bitmap",
 			    "Cannot read inode bitmap - "
 			    "block_group = %lu, inode_bitmap = %lu",
-			    block_group, (unsigned long) gdp->bg_inode_bitmap);
+			    block_group,
+			    (unsigned long) e_swab (bs, gdp->bg_inode_bitmap));
 	sb->u.ext2_sb.s_inode_bitmap_number[bitmap_nr] = block_group;
 	sb->u.ext2_sb.s_inode_bitmap[bitmap_nr] = bh;
 }
@@ -159,6 +162,7 @@
 	unsigned long block_group;
 	unsigned long bit;
 	int bitmap_nr;
+	int bs;
 	struct ext2_group_desc * gdp;
 	struct ext2_super_block * es;
 
@@ -187,8 +191,9 @@
 
 	sb = inode->i_sb;
 	lock_super (sb);
+	bs = BYTE_SWAP(inode->i_sb->u.ext2_sb.s_byte_swapped);
 	if (inode->i_ino < EXT2_FIRST_INO(sb) ||
-	    inode->i_ino > sb->u.ext2_sb.s_es->s_inodes_count) {
+	    inode->i_ino > e_swab (bs, sb->u.ext2_sb.s_es->s_inodes_count)) {
 		ext2_error (sb, "free_inode",
 			    "reserved inode or nonexistent inode");
 		unlock_super (sb);
@@ -199,16 +204,19 @@
 	bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb);
 	bitmap_nr = load_inode_bitmap (sb, block_group);
 	bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
-	if (!clear_bit (bit, bh->b_data))
+	if (!ext2_clear_bit (bit, bh->b_data))
 		ext2_warning (sb, "ext2_free_inode",
 			      "bit already cleared for inode %lu", inode->i_ino);
 	else {
 		gdp = get_group_desc (sb, block_group, &bh2);
-		gdp->bg_free_inodes_count++;
+		e_set_swab (bs, gdp->bg_free_inodes_count,
+			    e_swab (bs, gdp->bg_free_inodes_count) + 1);
 		if (S_ISDIR(inode->i_mode))
-			gdp->bg_used_dirs_count--;
+			e_set_swab (bs, gdp->bg_used_dirs_count,
+				    e_swab (bs, gdp->bg_used_dirs_count) - 1);
 		mark_buffer_dirty(bh2, 1);
-		es->s_free_inodes_count++;
+		e_set_swab (bs, es->s_free_inodes_count,
+			    e_swab (bs, es->s_free_inodes_count) + 1);
 		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 		inode->i_dirt = 0;
 	}
@@ -257,6 +265,7 @@
 	int i, j, avefreei;
 	struct inode * inode;
 	int bitmap_nr;
+	int bs;
 	struct ext2_group_desc * gdp;
 	struct ext2_group_desc * tmp;
 	struct ext2_super_block * es;
@@ -267,20 +276,21 @@
 	inode->i_sb = sb;
 	inode->i_flags = sb->s_flags;
 	lock_super (sb);
+	bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 	es = sb->u.ext2_sb.s_es;
 repeat:
 	gdp = NULL; i=0;
 	
 	*err = -ENOSPC;
 	if (S_ISDIR(mode)) {
-		avefreei = es->s_free_inodes_count /
+		avefreei = e_swab (bs, es->s_free_inodes_count) /
 			sb->u.ext2_sb.s_groups_count;
 /* I am not yet convinced that this next bit is necessary.
 		i = dir->u.ext2_i.i_block_group;
 		for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) {
 			tmp = get_group_desc (sb, i, &bh2);
-			if ((tmp->bg_used_dirs_count << 8) < 
-			    tmp->bg_free_inodes_count) {
+			if ((e_swab (bs, tmp->bg_used_dirs_count) << 8) < 
+			    e_swab (bs, tmp->bg_free_inodes_count)) {
 				gdp = tmp;
 				break;
 			}
@@ -291,11 +301,11 @@
 		if (!gdp) {
 			for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) {
 				tmp = get_group_desc (sb, j, &bh2);
-				if (tmp->bg_free_inodes_count &&
-					tmp->bg_free_inodes_count >= avefreei) {
+				if (e_swab (bs, tmp->bg_free_inodes_count) &&
+					e_swab (bs, tmp->bg_free_inodes_count) >= avefreei) {
 					if (!gdp || 
-					    (tmp->bg_free_blocks_count >
-					     gdp->bg_free_blocks_count)) {
+					    (e_swab (bs, tmp->bg_free_blocks_count) >
+					     e_swab (bs, gdp->bg_free_blocks_count))) {
 						i = j;
 						gdp = tmp;
 					}
@@ -353,10 +363,10 @@
 	}
 	bitmap_nr = load_inode_bitmap (sb, i);
 	bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
-	if ((j = find_first_zero_bit ((unsigned long *) bh->b_data,
+	if ((j = ext2_find_first_zero_bit ((unsigned long *) bh->b_data,
 				      EXT2_INODES_PER_GROUP(sb))) <
 	    EXT2_INODES_PER_GROUP(sb)) {
-		if (set_bit (j, bh->b_data)) {
+		if (ext2_set_bit (j, bh->b_data)) {
 			ext2_warning (sb, "ext2_new_inode",
 				      "bit already set for inode %d", j);
 			goto repeat;
@@ -378,7 +388,7 @@
 		goto repeat;
 	}
 	j += i * EXT2_INODES_PER_GROUP(sb) + 1;
-	if (j < EXT2_FIRST_INO(sb) || j > es->s_inodes_count) {
+	if (j < EXT2_FIRST_INO(sb) || j > e_swab (bs, es->s_inodes_count)) {
 		ext2_error (sb, "ext2_new_inode",
 			    "reserved inode or inode > inodes count - "
 			    "block_group = %d,inode=%d", i, j);
@@ -386,11 +396,14 @@
 		iput (inode);
 		return NULL;
 	}
-	gdp->bg_free_inodes_count--;
+	e_set_swab (bs, gdp->bg_free_inodes_count,
+		    e_swab (bs, gdp->bg_free_inodes_count) - 1);
 	if (S_ISDIR(mode))
-		gdp->bg_used_dirs_count++;
+		e_set_swab (bs, gdp->bg_used_dirs_count,
+			    e_swab (bs, gdp->bg_used_dirs_count) + 1);
 	mark_buffer_dirty(bh2, 1);
-	es->s_free_inodes_count--;
+	e_set_swab (bs, es->s_free_inodes_count,
+		    e_swab (bs, es->s_free_inodes_count) - 1);
 	mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 	sb->s_dirt = 1;
 	inode->i_mode = mode;
@@ -454,29 +467,31 @@
 	unsigned long desc_count, bitmap_count, x;
 	int bitmap_nr;
 	struct ext2_group_desc * gdp;
-	int i;
+	int i, bs;
 
 	lock_super (sb);
 	es = sb->u.ext2_sb.s_es;
+	bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 	desc_count = 0;
 	bitmap_count = 0;
 	gdp = NULL;
 	for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
 		gdp = get_group_desc (sb, i, NULL);
-		desc_count += gdp->bg_free_inodes_count;
+		desc_count += e_swab (bs, gdp->bg_free_inodes_count);
 		bitmap_nr = load_inode_bitmap (sb, i);
 		x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
 				     EXT2_INODES_PER_GROUP(sb) / 8);
 		printk ("group %d: stored = %d, counted = %lu\n",
-			i, gdp->bg_free_inodes_count, x);
+			i, e_swab (bs, gdp->bg_free_inodes_count), x);
 		bitmap_count += x;
 	}
 	printk("ext2_count_free_inodes: stored = %lu, computed = %lu, %lu\n",
-		es->s_free_inodes_count, desc_count, bitmap_count);
+		e_swab (bs, es->s_free_inodes_count), desc_count, bitmap_count);
 	unlock_super (sb);
 	return desc_count;
 #else
-	return sb->u.ext2_sb.s_es->s_free_inodes_count;
+	return e_swab (BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped),
+		       sb->u.ext2_sb.s_es->s_free_inodes_count);
 #endif
 }
 
@@ -486,30 +501,32 @@
 	unsigned long desc_count, bitmap_count, x;
 	int bitmap_nr;
 	struct ext2_group_desc * gdp;
-	int i;
+	int i, bs;
 
 	lock_super (sb);
+	bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 	es = sb->u.ext2_sb.s_es;
 	desc_count = 0;
 	bitmap_count = 0;
 	gdp = NULL;
 	for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
 		gdp = get_group_desc (sb, i, NULL);
-		desc_count += gdp->bg_free_inodes_count;
+		desc_count += e_swab (bs, gdp->bg_free_inodes_count);
 		bitmap_nr = load_inode_bitmap (sb, i);
 		x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
 				     EXT2_INODES_PER_GROUP(sb) / 8);
-		if (gdp->bg_free_inodes_count != x)
+		if (e_swab (bs, gdp->bg_free_inodes_count) != x)
 			ext2_error (sb, "ext2_check_inodes_bitmap",
 				    "Wrong free inodes count in group %d, "
 				    "stored = %d, counted = %lu", i,
-				    gdp->bg_free_inodes_count, x);
+				    e_swab (bs, gdp->bg_free_inodes_count), x);
 		bitmap_count += x;
 	}
-	if (es->s_free_inodes_count != bitmap_count)
+	if (e_swab (bs, es->s_free_inodes_count) != bitmap_count)
 		ext2_error (sb, "ext2_check_inodes_bitmap",
 			    "Wrong free inodes count in super block, "
 			    "stored = %lu, counted = %lu",
-			    (unsigned long) es->s_free_inodes_count, bitmap_count);
+			    (unsigned long) e_swab (bs, es->s_free_inodes_count),
+			    bitmap_count);
 	unlock_super (sb);
 }
diff -urN pc-2.0.21/fs/ext2/inode.c linux-2.0.21/fs/ext2/inode.c
--- pc-2.0.21/fs/ext2/inode.c	Sat May  4 09:06:18 1996
+++ linux-2.0.21/fs/ext2/inode.c	Wed Sep 11 20:29:52 1996
@@ -46,13 +46,13 @@
 
 #define inode_bmap(inode, nr) ((inode)->u.ext2_i.i_data[(nr)])
 
-static inline int block_bmap (struct buffer_head * bh, int nr)
+static inline int block_bmap (struct buffer_head * bh, int nr, int bs)
 {
 	int tmp;
 
 	if (!bh)
 		return 0;
-	tmp = ((u32 *) bh->b_data)[nr];
+	tmp = e_swab (bs, ((u32 *) bh->b_data)[nr]);
 	brelse (bh);
 	return tmp;
 }
@@ -134,6 +134,7 @@
 	int i;
 	int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
 	int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
+	int bs = BYTE_SWAP(inode->i_sb->u.ext2_sb.s_byte_swapped);
 
 	if (block < 0) {
 		ext2_warning (inode->i_sb, "ext2_bmap", "block < 0");
@@ -153,7 +154,7 @@
 		if (!i)
 			return 0;
 		return block_bmap (bread (inode->i_dev, i,
-					  inode->i_sb->s_blocksize), block);
+					  inode->i_sb->s_blocksize), block, bs);
 	}
 	block -= addr_per_block;
 	if (block < (1 << (addr_per_block_bits * 2))) {
@@ -162,27 +163,28 @@
 			return 0;
 		i = block_bmap (bread (inode->i_dev, i,
 				       inode->i_sb->s_blocksize),
-				block >> addr_per_block_bits);
+				block >> addr_per_block_bits, bs);
 		if (!i)
 			return 0;
 		return block_bmap (bread (inode->i_dev, i,
 					  inode->i_sb->s_blocksize),
-				   block & (addr_per_block - 1));
+				   block & (addr_per_block - 1), bs);
 	}
 	block -= (1 << (addr_per_block_bits * 2));
 	i = inode_bmap (inode, EXT2_TIND_BLOCK);
 	if (!i)
 		return 0;
 	i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
-			block >> (addr_per_block_bits * 2));
+			block >> (addr_per_block_bits * 2), bs);
 	if (!i)
 		return 0;
 	i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
-			(block >> addr_per_block_bits) & (addr_per_block - 1));
+			(block >> addr_per_block_bits) & (addr_per_block - 1),
+			bs);
 	if (!i)
 		return 0;
 	return block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
-			   block & (addr_per_block - 1));
+			   block & (addr_per_block - 1), bs);
 }
 
 static struct buffer_head * inode_getblk (struct inode * inode, int nr,
@@ -192,6 +194,7 @@
 	int tmp, goal = 0;
 	struct buffer_head * result;
 	int blocks = inode->i_sb->s_blocksize / 512;
+	int bs = BYTE_SWAP(inode->i_sb->u.ext2_sb.s_byte_swapped);
 
 	p = inode->u.ext2_i.i_data + nr;
 repeat:
@@ -224,7 +227,7 @@
 		if (!goal)
 			goal = (inode->u.ext2_i.i_block_group * 
 				EXT2_BLOCKS_PER_GROUP(inode->i_sb)) +
-			       inode->i_sb->u.ext2_sb.s_es->s_first_data_block;
+			       e_swab (bs, inode->i_sb->u.ext2_sb.s_es->s_first_data_block);
 	}
 
 	ext2_debug ("goal = %d.\n", goal);
@@ -259,6 +262,7 @@
 	u32 * p;
 	struct buffer_head * result;
 	int blocks = inode->i_sb->s_blocksize / 512;
+	int bs = BYTE_SWAP(inode->i_sb->u.ext2_sb.s_byte_swapped);
 
 	if (!bh)
 		return NULL;
@@ -274,7 +278,7 @@
 repeat:
 	tmp = *p;
 	if (tmp) {
-		result = getblk (bh->b_dev, tmp, blocksize);
+		result = getblk (bh->b_dev, e_swab (bs, *p), blocksize);
 		if (tmp == *p) {
 			brelse (bh);
 			return result;
@@ -294,7 +298,7 @@
 	if (!goal) {
 		for (tmp = nr - 1; tmp >= 0; tmp--) {
 			if (((u32 *) bh->b_data)[tmp]) {
-				goal = ((u32 *)bh->b_data)[tmp];
+				goal = e_swab (bs, ((u32 *)bh->b_data)[tmp]);
 				break;
 			}
 		}
@@ -312,7 +316,7 @@
 		brelse (result);
 		goto repeat;
 	}
-	*p = tmp;
+	e_set_swab (bs, *p, tmp);
 	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) {
 		ll_rw_block (WRITE, 1, &bh);
@@ -335,6 +339,7 @@
 	int firstblock = 0;
 	int result = 0;
 	int i;
+	int bs = BYTE_SWAP(inode->i_sb->u.ext2_sb.s_byte_swapped);
 
 	/* Check to see if clustering possible here. */
 
@@ -351,12 +356,15 @@
 	  if(*p == 0) goto out;
 	  
 	  /* See if aligned correctly */
-	  if(i==0) firstblock = *p;
-	  else if(*p != firstblock + i) goto out;
+	  if(i==0) firstblock = e_swab(bs, *p);
+	  else if(e_swab(bs, *p) != firstblock + i) goto out;
 	}
 	
 	p = (u32 *) bh->b_data + nr;
-	result = generate_cluster(bh->b_dev, (int *) p, blocksize);
+	if (bs)
+	  result = generate_cluster_swab(bh->b_dev, (int *) p, blocksize);
+	else
+	  result = generate_cluster(bh->b_dev, (int *) p, blocksize);
 
       out:
 	brelse(bh);
@@ -501,14 +509,16 @@
 	unsigned long desc;
 	unsigned long block;
 	unsigned long offset;
+	int bs = BYTE_SWAP(inode->i_sb->u.ext2_sb.s_byte_swapped);
 	struct ext2_group_desc * gdp;
 
 	if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO &&
 	     inode->i_ino != EXT2_ACL_DATA_INO &&
 	     inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
-	    inode->i_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) {
+	    inode->i_ino > e_swab (bs, inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) {
 		ext2_error (inode->i_sb, "ext2_read_inode",
 			    "bad inode number: %lu", inode->i_ino);
+		inode->i_op = NULL;
 		return;
 	}
 	block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
@@ -527,7 +537,7 @@
 	 */
 	offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *
 		EXT2_INODE_SIZE(inode->i_sb);
-	block = gdp[desc].bg_inode_table +
+	block = e_swab (bs, gdp[desc].bg_inode_table) +
 		(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
 	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
 		ext2_panic (inode->i_sb, "ext2_read_inode",
@@ -536,27 +546,27 @@
 	offset &= (EXT2_BLOCK_SIZE(inode->i_sb) - 1);
 	raw_inode = (struct ext2_inode *) (bh->b_data + offset);
 
-	inode->i_mode = raw_inode->i_mode;
-	inode->i_uid = raw_inode->i_uid;
-	inode->i_gid = raw_inode->i_gid;
-	inode->i_nlink = raw_inode->i_links_count;
-	inode->i_size = raw_inode->i_size;
-	inode->i_atime = raw_inode->i_atime;
-	inode->i_ctime = raw_inode->i_ctime;
-	inode->i_mtime = raw_inode->i_mtime;
-	inode->u.ext2_i.i_dtime = raw_inode->i_dtime;
+	inode->i_mode = e_swab (bs, raw_inode->i_mode);
+	inode->i_uid = e_swab (bs, raw_inode->i_uid);
+	inode->i_gid = e_swab (bs, raw_inode->i_gid);
+	inode->i_nlink = e_swab (bs, raw_inode->i_links_count);
+	inode->i_size = e_swab (bs, raw_inode->i_size);
+	inode->i_atime = e_swab (bs, raw_inode->i_atime);
+	inode->i_ctime = e_swab (bs, raw_inode->i_ctime);
+	inode->i_mtime = e_swab (bs, raw_inode->i_mtime);
+	inode->u.ext2_i.i_dtime = e_swab (bs, raw_inode->i_dtime);
 	inode->i_blksize = PAGE_SIZE;	/* This is the optimal IO size (for stat), not the fs block size */
-	inode->i_blocks = raw_inode->i_blocks;
+	inode->i_blocks = e_swab (bs, raw_inode->i_blocks);
 	inode->i_version = ++event;
 	inode->u.ext2_i.i_new_inode = 0;
-	inode->u.ext2_i.i_flags = raw_inode->i_flags;
-	inode->u.ext2_i.i_faddr = raw_inode->i_faddr;
-	inode->u.ext2_i.i_frag_no = raw_inode->i_frag;
-	inode->u.ext2_i.i_frag_size = raw_inode->i_fsize;
+	inode->u.ext2_i.i_flags = e_swab (bs, raw_inode->i_flags);
+	inode->u.ext2_i.i_faddr = e_swab (bs, raw_inode->i_faddr);
+	inode->u.ext2_i.i_frag_no = e_swab (bs, raw_inode->i_frag);
+	inode->u.ext2_i.i_frag_size = e_swab (bs, raw_inode->i_fsize);
 	inode->u.ext2_i.i_osync = 0;
-	inode->u.ext2_i.i_file_acl = raw_inode->i_file_acl;
-	inode->u.ext2_i.i_dir_acl = raw_inode->i_dir_acl;
-	inode->u.ext2_i.i_version = raw_inode->i_version;
+	inode->u.ext2_i.i_file_acl = e_swab (bs, raw_inode->i_file_acl);
+	inode->u.ext2_i.i_dir_acl = e_swab (bs, raw_inode->i_dir_acl);
+	inode->u.ext2_i.i_version = e_swab (bs, raw_inode->i_version);
 	inode->u.ext2_i.i_block_group = block_group;
 	inode->u.ext2_i.i_next_alloc_block = 0;
 	inode->u.ext2_i.i_next_alloc_goal = 0;
@@ -564,9 +574,15 @@
 		ext2_error (inode->i_sb, "ext2_read_inode",
 			    "New inode has non-zero prealloc count!");
 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
-		inode->i_rdev = to_kdev_t(raw_inode->i_block[0]);
+		inode->i_rdev = to_kdev_t(e_swab (bs, raw_inode->i_block[0]));
+	/*
+	 * if no blocks are allocated, then no pointers have to be
+	 * swapped. This is neccessary for fast symlinks, and (later) for 
+	 * files small enough to fit in the inode.
+	 */
 	else for (block = 0; block < EXT2_N_BLOCKS; block++)
-		inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
+		inode->u.ext2_i.i_data[block] =
+			e_swab ((inode->i_blocks && bs), raw_inode->i_block[block]);
 	brelse (bh);
 	inode->i_op = NULL;
 	if (inode->i_ino == EXT2_ACL_IDX_INO ||
@@ -602,11 +618,12 @@
 	unsigned long block;
 	unsigned long offset;
 	int err = 0;
+	int bs = BYTE_SWAP(inode->i_sb->u.ext2_sb.s_byte_swapped);
 	struct ext2_group_desc * gdp;
 
 	if ((inode->i_ino != EXT2_ROOT_INO &&
 	     inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
-	    inode->i_ino > inode->i_sb->u.ext2_sb.s_es->s_inodes_count) {
+	    inode->i_ino > e_swab (bs, inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) {
 		ext2_error (inode->i_sb, "ext2_write_inode",
 			    "bad inode number: %lu", inode->i_ino);
 		return 0;
@@ -627,7 +644,7 @@
 	 */
 	offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *
 		EXT2_INODE_SIZE(inode->i_sb);
-	block = gdp[desc].bg_inode_table +
+	block = e_swab (bs, gdp[desc].bg_inode_table) +
 		(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
 	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
 		ext2_panic (inode->i_sb, "ext2_write_inode",
@@ -636,27 +653,32 @@
 	offset &= EXT2_BLOCK_SIZE(inode->i_sb) - 1;
 	raw_inode = (struct ext2_inode *) (bh->b_data + offset);
 
-	raw_inode->i_mode = inode->i_mode;
-	raw_inode->i_uid = inode->i_uid;
-	raw_inode->i_gid = inode->i_gid;
-	raw_inode->i_links_count = inode->i_nlink;
-	raw_inode->i_size = inode->i_size;
-	raw_inode->i_atime = inode->i_atime;
-	raw_inode->i_ctime = inode->i_ctime;
-	raw_inode->i_mtime = inode->i_mtime;
-	raw_inode->i_blocks = inode->i_blocks;
-	raw_inode->i_dtime = inode->u.ext2_i.i_dtime;
-	raw_inode->i_flags = inode->u.ext2_i.i_flags;
-	raw_inode->i_faddr = inode->u.ext2_i.i_faddr;
-	raw_inode->i_frag = inode->u.ext2_i.i_frag_no;
-	raw_inode->i_fsize = inode->u.ext2_i.i_frag_size;
-	raw_inode->i_file_acl = inode->u.ext2_i.i_file_acl;
-	raw_inode->i_dir_acl = inode->u.ext2_i.i_dir_acl;
-	raw_inode->i_version = inode->u.ext2_i.i_version;
+	e_set_swab (bs, raw_inode->i_mode, inode->i_mode);
+	e_set_swab (bs, raw_inode->i_uid, inode->i_uid);
+	e_set_swab (bs, raw_inode->i_gid, inode->i_gid);
+	e_set_swab (bs, raw_inode->i_links_count, inode->i_nlink);
+	e_set_swab (bs, raw_inode->i_size, inode->i_size);
+	e_set_swab (bs, raw_inode->i_atime, inode->i_atime);
+	e_set_swab (bs, raw_inode->i_ctime, inode->i_ctime);
+	e_set_swab (bs, raw_inode->i_mtime, inode->i_mtime);
+	e_set_swab (bs, raw_inode->i_blocks, inode->i_blocks);
+	e_set_swab (bs, raw_inode->i_dtime, inode->u.ext2_i.i_dtime);
+	e_set_swab (bs, raw_inode->i_flags, inode->u.ext2_i.i_flags);
+	e_set_swab (bs, raw_inode->i_faddr, inode->u.ext2_i.i_faddr);
+	e_set_swab (bs, raw_inode->i_frag, inode->u.ext2_i.i_frag_no);
+	e_set_swab (bs, raw_inode->i_fsize, inode->u.ext2_i.i_frag_size);
+	e_set_swab (bs, raw_inode->i_file_acl, inode->u.ext2_i.i_file_acl);
+	e_set_swab (bs, raw_inode->i_dir_acl, inode->u.ext2_i.i_dir_acl);
+	e_set_swab (bs, raw_inode->i_version, inode->u.ext2_i.i_version);
 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
-		raw_inode->i_block[0] = kdev_t_to_nr(inode->i_rdev);
+		e_set_swab (bs, raw_inode->i_block[0], kdev_t_to_nr(inode->i_rdev));
+	/*
+	 * if no blocks are allocated, then no pointers have to be
+	 * swapped. This is neccessary for fast symlinks, and (later) for 
+	 * files small enough to fit in the inode.
+	 */
 	else for (block = 0; block < EXT2_N_BLOCKS; block++)
-		raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
+		e_set_swab ((inode->i_blocks && bs), raw_inode->i_block[block], inode->u.ext2_i.i_data[block]);
 	mark_buffer_dirty(bh, 1);
 	inode->i_dirt = 0;
 	if (do_sync) {
diff -urN pc-2.0.21/fs/ext2/namei.c linux-2.0.21/fs/ext2/namei.c
--- pc-2.0.21/fs/ext2/namei.c	Fri Sep 20 12:17:18 1996
+++ linux-2.0.21/fs/ext2/namei.c	Mon Sep 23 22:31:58 1996
@@ -36,17 +36,17 @@
  * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure.
  */
 static int ext2_match (int len, const char * const name,
-		       struct ext2_dir_entry * de)
+		       struct ext2_dir_entry * de, int bs)
 {
 	if (!de || !de->inode || len > EXT2_NAME_LEN)
 		return 0;
 	/*
 	 * "" means "." ---> so paths like "/usr/lib//libc.a" work
 	 */
-	if (!len && de->name_len == 1 && (de->name[0] == '.') &&
+	if (!len && e_swab (bs, de->name_len) == 1 && (de->name[0] == '.') &&
 	   (de->name[1] == '\0'))
 		return 1;
-	if (len != de->name_len)
+	if (len != e_swab (bs, de->name_len))
 		return 0;
 	return !memcmp(name, de->name, len);
 }
@@ -67,12 +67,13 @@
 	struct buffer_head * bh_use[NAMEI_RA_SIZE];
 	struct buffer_head * bh_read[NAMEI_RA_SIZE];
 	unsigned long offset;
-	int block, toread, i, err;
+	int block, toread, i, err, bs;
 
 	*res_dir = NULL;
 	if (!dir)
 		return NULL;
 	sb = dir->i_sb;
+	bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 
 	if (namelen > EXT2_NAME_LEN)
 		return NULL;
@@ -119,9 +120,9 @@
 		dlimit = bh->b_data + sb->s_blocksize;
 		while ((char *) de < dlimit) {
 			if (!ext2_check_dir_entry ("ext2_find_entry", dir,
-						   de, bh, offset))
+						   de, bh, offset, bs))
 				goto failure;
-			if (de->inode != 0 && ext2_match (namelen, name, de)) {
+			if (de->inode != 0 && ext2_match (namelen, name, de, bs)) {
 				for (i = 0; i < NAMEI_RA_SIZE; ++i) {
 					if (bh_use[i] != bh)
 						brelse (bh_use[i]);
@@ -129,9 +130,9 @@
 				*res_dir = de;
 				return bh;
 			}
-			offset += de->rec_len;
+			offset += e_swab (bs, de->rec_len);
 			de = (struct ext2_dir_entry *)
-				((char *) de + de->rec_len);
+				((char *) de + e_swab (bs, de->rec_len));
 		}
 
 		brelse (bh);
@@ -188,7 +189,7 @@
 		iput (dir);
 		return -ENOENT;
 	}
-	ino = de->inode;
+	ino = e_swab (BYTE_SWAP(dir->i_sb->u.ext2_sb.s_byte_swapped), de->inode);
 	dcache_add(dir, name, len, ino);
 	brelse (bh);
 	if (!(*result = iget (dir->i_sb, ino))) {
@@ -219,12 +220,14 @@
 	struct buffer_head * bh;
 	struct ext2_dir_entry * de, * de1;
 	struct super_block * sb;
+	int bs;
 
 	*err = -EINVAL;
 	*res_dir = NULL;
 	if (!dir)
 		return NULL;
 	sb = dir->i_sb;
+	bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 
 	if (namelen > EXT2_NAME_LEN)
 	{
@@ -266,7 +269,7 @@
 
 				de = (struct ext2_dir_entry *) bh->b_data;
 				de->inode = 0;
-				de->rec_len = sb->s_blocksize;
+				e_set_swab (bs, de->rec_len, sb->s_blocksize);
 				dir->i_size = offset + sb->s_blocksize;
 				dir->i_dirt = 1;
 			} else {
@@ -277,29 +280,32 @@
 			}
 		}
 		if (!ext2_check_dir_entry ("ext2_add_entry", dir, de, bh,
-					   offset)) {
+					   offset, bs)) {
 			*err = -ENOENT;
 			brelse (bh);
 			return NULL;
 		}
-		if (de->inode != 0 && ext2_match (namelen, name, de)) {
+		if (de->inode != 0 && ext2_match (namelen, name, de, bs)) {
 				*err = -EEXIST;
 				brelse (bh);
 				return NULL;
 		}
-		if ((de->inode == 0 && de->rec_len >= rec_len) ||
-		    (de->rec_len >= EXT2_DIR_REC_LEN(de->name_len) + rec_len)) {
-			offset += de->rec_len;
+		if ((de->inode == 0 && e_swab (bs, de->rec_len) >= rec_len) ||
+		    (e_swab (bs, de->rec_len) >=
+		     EXT2_DIR_REC_LEN(e_swab (bs, de->name_len)) + rec_len)) {
+			offset += e_swab (bs, de->rec_len);
 			if (de->inode) {
 				de1 = (struct ext2_dir_entry *) ((char *) de +
-					EXT2_DIR_REC_LEN(de->name_len));
-				de1->rec_len = de->rec_len -
-					EXT2_DIR_REC_LEN(de->name_len);
-				de->rec_len = EXT2_DIR_REC_LEN(de->name_len);
+					EXT2_DIR_REC_LEN(e_swab (bs, de->name_len)));
+				e_set_swab (bs, de1->rec_len,
+					    e_swab (bs, de->rec_len) -
+					    EXT2_DIR_REC_LEN(e_swab (bs, de->name_len)));
+				e_set_swab (bs, de->rec_len,
+					    EXT2_DIR_REC_LEN(e_swab (bs, de->name_len)));
 				de = de1;
 			}
 			de->inode = 0;
-			de->name_len = namelen;
+			e_set_swab (bs, de->name_len, namelen);
 			memcpy (de->name, name, namelen);
 			/*
 			 * XXX shouldn't update any times until successful
@@ -320,8 +326,8 @@
 			*err = 0;
 			return bh;
 		}
-		offset += de->rec_len;
-		de = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
+		offset += e_swab (bs, de->rec_len);
+		de = (struct ext2_dir_entry *) ((char *) de + e_swab (bs, de->rec_len));
 	}
 	brelse (bh);
 	return NULL;
@@ -332,7 +338,7 @@
  * previous entry
  */
 static int ext2_delete_entry (struct ext2_dir_entry * dir,
-			      struct buffer_head * bh)
+			      struct buffer_head * bh, int bs)
 {
 	struct ext2_dir_entry * de, * pde;
 	int i;
@@ -342,17 +348,19 @@
 	de = (struct ext2_dir_entry *) bh->b_data;
 	while (i < bh->b_size) {
 		if (!ext2_check_dir_entry ("ext2_delete_entry", NULL, 
-					   de, bh, i))
+					   de, bh, i, bs))
 			return -EIO;
 		if (de == dir)  {
 			if (pde)
-				pde->rec_len += dir->rec_len;
+				e_set_swab (bs, pde->rec_len,
+					    e_swab (bs, pde->rec_len) +
+					    e_swab (bs, dir->rec_len));
 			dir->inode = 0;
 			return 0;
 		}
-		i += de->rec_len;
+		i += e_swab (bs, de->rec_len);
 		pde = de;
-		de = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
+		de = (struct ext2_dir_entry *) ((char *) de + e_swab (bs, de->rec_len));
 	}
 	return -ENOENT;
 }
@@ -364,6 +372,7 @@
 	struct buffer_head * bh;
 	struct ext2_dir_entry * de;
 	int err;
+	int bs = BYTE_SWAP(dir->i_sb->u.ext2_sb.s_byte_swapped);
 
 	*result = NULL;
 	if (!dir)
@@ -384,9 +393,9 @@
 		iput (dir);
 		return err;
 	}
-	de->inode = inode->i_ino;
+	e_set_swab (bs, de->inode, inode->i_ino);
 	dir->i_version = ++event;
-	dcache_add(dir, de->name, de->name_len, de->inode);
+	dcache_add(dir, de->name, e_swab (bs, de->name_len), inode->i_ino);
 	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(dir)) {
 		ll_rw_block (WRITE, 1, &bh);
@@ -405,6 +414,7 @@
 	struct buffer_head * bh;
 	struct ext2_dir_entry * de;
 	int err;
+	int bs = BYTE_SWAP(dir->i_sb->u.ext2_sb.s_byte_swapped);
 
 	if (!dir)
 		return -ENOENT;
@@ -453,9 +463,9 @@
 		iput (dir);
 		return err;
 	}
-	de->inode = inode->i_ino;
+	e_set_swab (bs, de->inode, inode->i_ino);
 	dir->i_version = ++event;
-	dcache_add(dir, de->name, de->name_len, de->inode);
+	dcache_add(dir, de->name, e_swab (bs, de->name_len), inode->i_ino);
 	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(dir)) {
 		ll_rw_block (WRITE, 1, &bh);
@@ -473,6 +483,7 @@
 	struct buffer_head * bh, * dir_block;
 	struct ext2_dir_entry * de;
 	int err;
+	int bs = BYTE_SWAP(dir->i_sb->u.ext2_sb.s_byte_swapped);
 
 	if (!dir)
 		return -ENOENT;
@@ -507,14 +518,14 @@
 	}
 	inode->i_blocks = inode->i_sb->s_blocksize / 512;
 	de = (struct ext2_dir_entry *) dir_block->b_data;
-	de->inode = inode->i_ino;
-	de->name_len = 1;
-	de->rec_len = EXT2_DIR_REC_LEN(de->name_len);
+	e_set_swab (bs, de->inode, inode->i_ino);
+	e_set_swab (bs, de->name_len, 1);
+	e_set_swab (bs, de->rec_len, EXT2_DIR_REC_LEN(e_swab (bs, de->name_len)));
 	strcpy (de->name, ".");
-	de = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
-	de->inode = dir->i_ino;
-	de->rec_len = inode->i_sb->s_blocksize - EXT2_DIR_REC_LEN(1);
-	de->name_len = 2;
+	de = (struct ext2_dir_entry *) ((char *) de + e_swab (bs, de->rec_len));
+	e_set_swab (bs, de->inode, dir->i_ino);
+	e_set_swab (bs, de->rec_len, inode->i_sb->s_blocksize - EXT2_DIR_REC_LEN(1));
+	e_set_swab (bs, de->name_len, 2);
 	strcpy (de->name, "..");
 	inode->i_nlink = 2;
 	mark_buffer_dirty(dir_block, 1);
@@ -531,9 +542,9 @@
 		iput (inode);
 		return err;
 	}
-	de->inode = inode->i_ino;
+	e_set_swab (bs, de->inode, inode->i_ino);
 	dir->i_version = ++event;
-	dcache_add(dir, de->name, de->name_len, de->inode);
+	dcache_add(dir, de->name, e_swab (bs, de->name_len), inode->i_ino);
 	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(dir)) {
 		ll_rw_block (WRITE, 1, &bh);
@@ -556,9 +567,10 @@
 	struct buffer_head * bh;
 	struct ext2_dir_entry * de, * de1;
 	struct super_block * sb;
-	int err;
+	int err, bs;
 
 	sb = inode->i_sb;
+	bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 	if (inode->i_size < EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) ||
 	    !(bh = ext2_bread (inode, 0, 0, &err))) {
 	    	ext2_warning (inode->i_sb, "empty_dir",
@@ -567,16 +579,16 @@
 		return 1;
 	}
 	de = (struct ext2_dir_entry *) bh->b_data;
-	de1 = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
-	if (de->inode != inode->i_ino || !de1->inode || 
+	de1 = (struct ext2_dir_entry *) ((char *) de + e_swab (bs, de->rec_len));
+	if (e_swab (bs, de->inode) != inode->i_ino || !de1->inode || 
 	    strcmp (".", de->name) || strcmp ("..", de1->name)) {
 	    	ext2_warning (inode->i_sb, "empty_dir",
 			      "bad directory (dir #%lu) - no `.' or `..'",
 			      inode->i_ino);
 		return 1;
 	}
-	offset = de->rec_len + de1->rec_len;
-	de = (struct ext2_dir_entry *) ((char *) de1 + de1->rec_len);
+	offset = e_swab (bs, de->rec_len) + e_swab (bs, de1->rec_len);
+	de = (struct ext2_dir_entry *) ((char *) de1 + e_swab (bs, de1->rec_len));
 	while (offset < inode->i_size ) {
 		if ((void *) de >= (void *) (bh->b_data + sb->s_blocksize)) {
 			brelse (bh);
@@ -591,7 +603,7 @@
 			de = (struct ext2_dir_entry *) bh->b_data;
 		}
 		if (!ext2_check_dir_entry ("empty_dir", inode, de, bh,
-					   offset)) {
+					   offset, bs)) {
 			brelse (bh);
 			return 1;
 		}
@@ -599,8 +611,8 @@
 			brelse (bh);
 			return 0;
 		}
-		offset += de->rec_len;
-		de = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
+		offset += e_swab (bs, de->rec_len);
+		de = (struct ext2_dir_entry *) ((char *) de + e_swab (bs, de->rec_len));
 	}
 	brelse (bh);
 	return 1;
@@ -612,6 +624,7 @@
 	struct inode * inode;
 	struct buffer_head * bh;
 	struct ext2_dir_entry * de;
+	int bs = BYTE_SWAP(dir->i_sb->u.ext2_sb.s_byte_swapped);
 
 repeat:
 	if (!dir)
@@ -626,7 +639,7 @@
 	if (!bh)
 		goto end_rmdir;
 	retval = -EPERM;
-	if (!(inode = iget (dir->i_sb, de->inode)))
+	if (!(inode = iget (dir->i_sb, e_swab (bs, de->inode))))
 		goto end_rmdir;
 	if (inode->i_sb->dq_op)
 		inode->i_sb->dq_op->initialize (inode, -1);
@@ -634,7 +647,7 @@
 		retval = -EBUSY;
 		goto end_rmdir;
 	}
-	if (de->inode != inode->i_ino) {
+	if (e_swab (bs, de->inode) != inode->i_ino) {
 		iput(inode);
 		brelse(bh);
 		current->counter = 0;
@@ -654,7 +667,7 @@
 	down(&inode->i_sem);
 	if (!empty_dir (inode))
 		retval = -ENOTEMPTY;
-	else if (de->inode != inode->i_ino)
+	else if (e_swab (bs, de->inode) != inode->i_ino)
 		retval = -ENOENT;
 	else {
 		if (inode->i_count > 1) {
@@ -667,7 +680,7 @@
 		 */
 			inode->i_size = 0;
 		}
-		retval = ext2_delete_entry (de, bh);
+		retval = ext2_delete_entry (de, bh, bs);
 		dir->i_version = ++event;
 	}
 	up(&inode->i_sem);
@@ -701,6 +714,7 @@
 	struct inode * inode;
 	struct buffer_head * bh;
 	struct ext2_dir_entry * de;
+	int bs = BYTE_SWAP(dir->i_sb->u.ext2_sb.s_byte_swapped);
 
 repeat:
 	if (!dir)
@@ -714,7 +728,7 @@
 	bh = ext2_find_entry (dir, name, len, &de);
 	if (!bh)
 		goto end_unlink;
-	if (!(inode = iget (dir->i_sb, de->inode)))
+	if (!(inode = iget (dir->i_sb, e_swab (bs, de->inode))))
 		goto end_unlink;
 	if (inode->i_sb->dq_op)
 		inode->i_sb->dq_op->initialize (inode, -1);
@@ -723,7 +737,7 @@
 		goto end_unlink;
 	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
 		goto end_unlink;
-	if (de->inode != inode->i_ino) {
+	if (e_swab (bs, de->inode) != inode->i_ino) {
 		iput(inode);
 		brelse(bh);
 		current->counter = 0;
@@ -740,7 +754,7 @@
 			      inode->i_ino, inode->i_nlink);
 		inode->i_nlink = 1;
 	}
-	retval = ext2_delete_entry (de, bh);
+	retval = ext2_delete_entry (de, bh, bs);
 	if (retval)
 		goto end_unlink;
 	dir->i_version = ++event;
@@ -772,6 +786,7 @@
 	int i, err;
 	int l;
 	char c;
+	int bs = BYTE_SWAP(dir->i_sb->u.ext2_sb.s_byte_swapped);
 
 	if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) {
 		iput (dir);
@@ -829,9 +844,9 @@
 		iput (dir);
 		return err;
 	}
-	de->inode = inode->i_ino;
+	e_set_swab (bs, de->inode, inode->i_ino);
 	dir->i_version = ++event;
-	dcache_add(dir, de->name, de->name_len, de->inode);
+	dcache_add(dir, de->name, e_swab (bs, de->name_len), inode->i_ino);
 	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(dir)) {
 		ll_rw_block (WRITE, 1, &bh);
@@ -849,6 +864,7 @@
 	struct ext2_dir_entry * de;
 	struct buffer_head * bh;
 	int err;
+	int bs = BYTE_SWAP(dir->i_sb->u.ext2_sb.s_byte_swapped);
 
 	if (S_ISDIR(oldinode->i_mode)) {
 		iput (oldinode);
@@ -878,9 +894,9 @@
 		iput (oldinode);
 		return err;
 	}
-	de->inode = oldinode->i_ino;
+	e_set_swab (bs, de->inode, oldinode->i_ino);
 	dir->i_version = ++event;
-	dcache_add(dir, de->name, de->name_len, de->inode);
+	dcache_add(dir, de->name, e_swab (bs, de->name_len), oldinode->i_ino);
 	mark_buffer_dirty(bh, 1);
 	if (IS_SYNC(dir)) {
 		ll_rw_block (WRITE, 1, &bh);
@@ -919,13 +935,13 @@
 	return result;
 }
 
-#define PARENT_INO(buffer) \
+#define PARENT_INO(bs, buffer) \
 	((struct ext2_dir_entry *) ((char *) buffer + \
-	((struct ext2_dir_entry *) buffer)->rec_len))->inode
+	e_swab (bs, ((struct ext2_dir_entry *) buffer)->rec_len)))->inode
 
-#define PARENT_NAME(buffer) \
+#define PARENT_NAME(bs, buffer) \
 	((struct ext2_dir_entry *) ((char *) buffer + \
-	((struct ext2_dir_entry *) buffer)->rec_len))->name
+	e_swab (bs, ((struct ext2_dir_entry *) buffer)->rec_len)))->name
 
 /*
  * rename uses retrying to avoid race-conditions: at least they should be
@@ -947,11 +963,12 @@
 	struct buffer_head * old_bh, * new_bh, * dir_bh;
 	struct ext2_dir_entry * old_de, * new_de;
 	int retval;
+	int bs = BYTE_SWAP(old_dir->i_sb->u.ext2_sb.s_byte_swapped);
 
 	goto start_up;
 try_again:
 	if (new_bh && new_de) {
-		ext2_delete_entry(new_de, new_bh);
+		ext2_delete_entry(new_de, new_bh, bs);
 		new_dir->i_version = ++event;
 	}
 	brelse (old_bh);
@@ -973,7 +990,7 @@
 	retval = -ENOENT;
 	if (!old_bh)
 		goto end_rename;
-	old_inode = __iget (old_dir->i_sb, old_de->inode, 0); /* don't cross mnt-points */
+	old_inode = __iget (old_dir->i_sb, e_swab (bs, old_de->inode), 0); /* don't cross mnt-points */
 	if (!old_inode)
 		goto end_rename;
 	if (must_be_dir && !S_ISDIR(old_inode->i_mode))
@@ -987,7 +1004,7 @@
 		goto end_rename;
 	new_bh = ext2_find_entry (new_dir, new_name, new_len, &new_de);
 	if (new_bh) {
-		new_inode = __iget (new_dir->i_sb, new_de->inode, 0); /* no mntp cross */
+		new_inode = __iget (new_dir->i_sb, e_swab (bs, new_de->inode), 0); /* no mntp cross */
 		if (!new_inode) {
 			brelse (new_bh);
 			new_bh = NULL;
@@ -1029,7 +1046,7 @@
 		dir_bh = ext2_bread (old_inode, 0, 0, &retval);
 		if (!dir_bh)
 			goto end_rename;
-		if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino)
+		if (e_swab (bs, PARENT_INO(bs, dir_bh->b_data)) != old_dir->i_ino)
 			goto end_rename;
 		retval = -EMLINK;
 		if (!new_inode && new_dir->i_nlink >= EXT2_LINK_MAX)
@@ -1044,18 +1061,19 @@
 	/*
 	 * sanity checking before doing the rename - avoid races
 	 */
-	if (new_inode && (new_de->inode != new_inode->i_ino))
+	if (new_inode && (e_swab (bs, new_de->inode) != new_inode->i_ino))
 		goto try_again;
 	if (new_de->inode && !new_inode)
 		goto try_again;
-	if (old_de->inode != old_inode->i_ino)
+	if (e_swab (bs, old_de->inode) != old_inode->i_ino)
 		goto try_again;
 	/*
 	 * ok, that's it
 	 */
-	new_de->inode = old_inode->i_ino;
-	dcache_add(new_dir, new_de->name, new_de->name_len, new_de->inode);
-	retval = ext2_delete_entry (old_de, old_bh);
+	e_set_swab (bs, new_de->inode, old_inode->i_ino);
+	dcache_add(new_dir, new_de->name, e_swab (bs, new_de->name_len),
+		   old_inode->i_ino);
+	retval = ext2_delete_entry (old_de, old_bh, bs);
 	if (retval == -ENOENT)
 		goto try_again;
 	if (retval)
@@ -1069,7 +1087,7 @@
 	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
 	old_dir->i_dirt = 1;
 	if (dir_bh) {
-		PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
+		e_set_swab (bs, PARENT_INO(bs, dir_bh->b_data), new_dir->i_ino);
 		dcache_add(old_inode, "..", 2, new_dir->i_ino);
 		mark_buffer_dirty(dir_bh, 1);
 		old_dir->i_nlink--;
diff -urN pc-2.0.21/fs/ext2/super.c linux-2.0.21/fs/ext2/super.c
--- pc-2.0.21/fs/ext2/super.c	Sun Jul  7 10:06:53 1996
+++ linux-2.0.21/fs/ext2/super.c	Sat Sep  7 00:23:28 1996
@@ -36,10 +36,11 @@
 		 const char * fmt, ...)
 {
 	va_list args;
+	int bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 
 	if (!(sb->s_flags & MS_RDONLY)) {
 		sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS;
-		sb->u.ext2_sb.s_es->s_state |= EXT2_ERROR_FS;
+		sb->u.ext2_sb.s_es->s_state |= (bs ? EXT2_ERROR_FS << 8 : EXT2_ERROR_FS);
 		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 		sb->s_dirt = 1;
 	}
@@ -47,14 +48,14 @@
 	vsprintf (error_buf, fmt, args);
 	va_end (args);
 	if (test_opt (sb, ERRORS_PANIC) ||
-	    (sb->u.ext2_sb.s_es->s_errors == EXT2_ERRORS_PANIC &&
+	    (e_swab (bs, sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_PANIC &&
 	     !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO)))
 		panic ("EXT2-fs panic (device %s): %s: %s\n",
 		       kdevname(sb->s_dev), function, error_buf);
 	printk (KERN_CRIT "EXT2-fs error (device %s): %s: %s\n",
 		kdevname(sb->s_dev), function, error_buf);
 	if (test_opt (sb, ERRORS_RO) ||
-	    (sb->u.ext2_sb.s_es->s_errors == EXT2_ERRORS_RO &&
+	    (e_swab (bs, sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_RO &&
 	     !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) {
 		printk ("Remounting filesystem read-only\n");
 		sb->s_flags |= MS_RDONLY;
@@ -68,7 +69,8 @@
 
 	if (!(sb->s_flags & MS_RDONLY)) {
 		sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS;
-		sb->u.ext2_sb.s_es->s_state |= EXT2_ERROR_FS;
+		sb->u.ext2_sb.s_es->s_state |=
+		  (BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped) ? EXT2_ERROR_FS << 8 : EXT2_ERROR_FS);
 		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 		sb->s_dirt = 1;
 	}
@@ -102,7 +104,9 @@
 
 	lock_super (sb);
 	if (!(sb->s_flags & MS_RDONLY)) {
-		sb->u.ext2_sb.s_es->s_state = sb->u.ext2_sb.s_mount_state;
+		e_set_swab (BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped),
+			    sb->u.ext2_sb.s_es->s_state,
+			    sb->u.ext2_sb.s_mount_state);
 		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 	}
 	sb->s_dev = 0;
@@ -270,7 +274,9 @@
 static void ext2_setup_super (struct super_block * sb,
 			      struct ext2_super_block * es)
 {
-	if (es->s_rev_level > EXT2_MAX_SUPP_REV) {
+	int bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
+
+	if (e_swab (bs, es->s_rev_level) > EXT2_MAX_SUPP_REV) {
 			printk ("EXT2-fs warning: revision level too high, "
 				"forcing read/only mode\n");
 			sb->s_flags |= MS_RDONLY;
@@ -282,19 +288,21 @@
 		else if ((sb->u.ext2_sb.s_mount_state & EXT2_ERROR_FS))
 			printk ("EXT2-fs warning: mounting fs with errors, "
 				"running e2fsck is recommended\n");
-		else if (es->s_max_mnt_count >= 0 &&
-		         es->s_mnt_count >= (unsigned short) es->s_max_mnt_count)
+		else if (e_swab (bs, es->s_max_mnt_count) >= 0 &&
+		         e_swab (bs, es->s_mnt_count) >=
+			 (unsigned short) e_swab (bs, es->s_max_mnt_count))
 			printk ("EXT2-fs warning: maximal mount count reached, "
 				"running e2fsck is recommended\n");
 		else if (es->s_checkinterval &&
-			(es->s_lastcheck + es->s_checkinterval <= CURRENT_TIME))
+			(e_swab (bs, es->s_lastcheck) + e_swab (bs, es->s_checkinterval) <= CURRENT_TIME))
 			printk ("EXT2-fs warning: checktime reached, "
 				"running e2fsck is recommended\n");
-		es->s_state &= ~EXT2_VALID_FS;
+		es->s_state &= ~(bs ? EXT2_VALID_FS << 8 : EXT2_VALID_FS);
 		if (!es->s_max_mnt_count)
-			es->s_max_mnt_count = EXT2_DFL_MAX_MNT_COUNT;
-		es->s_mnt_count++;
-		es->s_mtime = CURRENT_TIME;
+			e_set_swab (bs, es->s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT);
+		e_set_swab (bs, es->s_mnt_count,
+			    e_swab (bs, es->s_mnt_count) + 1);
+		e_set_swab (bs, es->s_mtime, CURRENT_TIME);
 		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 		sb->s_dirt = 1;
 		if (test_opt (sb, DEBUG))
@@ -317,7 +325,8 @@
 {
 	int i;
 	int desc_block = 0;
-	unsigned long block = sb->u.ext2_sb.s_es->s_first_data_block;
+	int bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
+	unsigned long block = e_swab (bs, sb->u.ext2_sb.s_es->s_first_data_block);
 	struct ext2_group_desc * gdp = NULL;
 
 	ext2_debug ("Checking group descriptors");
@@ -326,32 +335,32 @@
 	{
 		if ((i % EXT2_DESC_PER_BLOCK(sb)) == 0)
 			gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[desc_block++]->b_data;
-		if (gdp->bg_block_bitmap < block ||
-		    gdp->bg_block_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb))
+		if (e_swab (bs, gdp->bg_block_bitmap) < block ||
+		    e_swab (bs, gdp->bg_block_bitmap) >= block + EXT2_BLOCKS_PER_GROUP(sb))
 		{
 			ext2_error (sb, "ext2_check_descriptors",
 				    "Block bitmap for group %d"
 				    " not in group (block %lu)!",
-				    i, (unsigned long) gdp->bg_block_bitmap);
+				    i, (unsigned long) e_swab (bs, gdp->bg_block_bitmap));
 			return 0;
 		}
-		if (gdp->bg_inode_bitmap < block ||
-		    gdp->bg_inode_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb))
+		if (e_swab (bs, gdp->bg_inode_bitmap) < block ||
+		    e_swab (bs, gdp->bg_inode_bitmap) >= block + EXT2_BLOCKS_PER_GROUP(sb))
 		{
 			ext2_error (sb, "ext2_check_descriptors",
 				    "Inode bitmap for group %d"
 				    " not in group (block %lu)!",
-				    i, (unsigned long) gdp->bg_inode_bitmap);
+				    i, (unsigned long) e_swab (bs, gdp->bg_inode_bitmap));
 			return 0;
 		}
-		if (gdp->bg_inode_table < block ||
-		    gdp->bg_inode_table + sb->u.ext2_sb.s_itb_per_group >=
+		if (e_swab (bs, gdp->bg_inode_table) < block ||
+		    e_swab (bs, gdp->bg_inode_table) + sb->u.ext2_sb.s_itb_per_group >=
 		    block + EXT2_BLOCKS_PER_GROUP(sb))
 		{
 			ext2_error (sb, "ext2_check_descriptors",
 				    "Inode table for group %d"
 				    " not in group (block %lu)!",
-				    i, (unsigned long) gdp->bg_inode_table);
+				    i, (unsigned long) e_swab (bs, gdp->bg_inode_table));
 			return 0;
 		}
 		block += EXT2_BLOCKS_PER_GROUP(sb);
@@ -373,7 +382,7 @@
 	unsigned long logic_sb_block = 1;
 	kdev_t dev = sb->s_dev;
 	int db_count;
-	int i, j;
+	int i, j, bs;
 
 	sb->u.ext2_sb.s_mount_opt = 0;
 	set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL);
@@ -400,6 +409,13 @@
 	es = (struct ext2_super_block *) bh->b_data;
 	sb->u.ext2_sb.s_es = es;
 	sb->s_magic = es->s_magic;
+	sb->u.ext2_sb.s_byte_swapped = bs = 0;
+
+	if (sb->s_magic != EXT2_SUPER_MAGIC) {
+		sb->u.ext2_sb.s_byte_swapped = bs = 1;
+		sb->s_magic = e_swab (bs, es->s_magic);
+	}
+
 	if (sb->s_magic != EXT2_SUPER_MAGIC) {
 		if (!silent)
 			printk ("VFS: Can't find an ext2 filesystem on dev "
@@ -412,22 +428,22 @@
 		MOD_DEC_USE_COUNT;
 		return NULL;
 	}
-	if (es->s_rev_level > EXT2_GOOD_OLD_REV) {
-		if (es->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPP) {
+	if (e_swab (bs, es->s_rev_level) > EXT2_GOOD_OLD_REV) {
+		if (e_swab (bs, es->s_feature_incompat) & ~EXT2_FEATURE_INCOMPAT_SUPP) {
 			printk("EXT2-fs: %s: couldn't mount because of "
 			       "unsupported optional features.\n", 
 			       kdevname(dev));
 			goto failed_mount;
 		}
 		if (!(sb->s_flags & MS_RDONLY) &&
-		    (es->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPP)) {
+		    (e_swab (bs, es->s_feature_ro_compat) & ~EXT2_FEATURE_RO_COMPAT_SUPP)) {
 			printk("EXT2-fs: %s: couldn't mount RDWR because of "
 			       "unsupported optional features.\n", 
 			       kdevname(dev));
 			goto failed_mount;
 		}
 	}
-	sb->s_blocksize_bits = sb->u.ext2_sb.s_es->s_log_block_size + 10;
+	sb->s_blocksize_bits = e_swab (bs, es->s_log_block_size) + 10;
 	sb->s_blocksize = 1 << sb->s_blocksize_bits;
 	if (sb->s_blocksize != BLOCK_SIZE && 
 	    (sb->s_blocksize == 1024 || sb->s_blocksize == 2048 ||  
@@ -446,17 +462,17 @@
 		}
 		es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
 		sb->u.ext2_sb.s_es = es;
-		if (es->s_magic != EXT2_SUPER_MAGIC) {
+		if (e_swab (bs, es->s_magic) != EXT2_SUPER_MAGIC) {
 			printk ("EXT2-fs: Magic mismatch, very weird !\n");
 			goto failed_mount;
 		}
 	}
-	if (es->s_rev_level == EXT2_GOOD_OLD_REV) {
+	if (e_swab (bs, es->s_rev_level) == EXT2_GOOD_OLD_REV) {
 		sb->u.ext2_sb.s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
 		sb->u.ext2_sb.s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
 	} else {
-		sb->u.ext2_sb.s_inode_size = es->s_inode_size;
-		sb->u.ext2_sb.s_first_ino = es->s_first_ino;
+		sb->u.ext2_sb.s_inode_size = e_swab (bs, es->s_inode_size);
+		sb->u.ext2_sb.s_first_ino = e_swab (bs, es->s_first_ino);
 		if (sb->u.ext2_sb.s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) {
 			printk ("EXT2-fs: unsupported inode size: %d\n",
 				sb->u.ext2_sb.s_inode_size);
@@ -464,15 +480,15 @@
 		}
 	}
 	sb->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
-				   es->s_log_frag_size;
+				   e_swab (bs, es->s_log_frag_size);
 	if (sb->u.ext2_sb.s_frag_size)
 		sb->u.ext2_sb.s_frags_per_block = sb->s_blocksize /
 						  sb->u.ext2_sb.s_frag_size;
 	else
 		sb->s_magic = 0;
-	sb->u.ext2_sb.s_blocks_per_group = es->s_blocks_per_group;
-	sb->u.ext2_sb.s_frags_per_group = es->s_frags_per_group;
-	sb->u.ext2_sb.s_inodes_per_group = es->s_inodes_per_group;
+	sb->u.ext2_sb.s_blocks_per_group = e_swab (bs, es->s_blocks_per_group);
+	sb->u.ext2_sb.s_frags_per_group = e_swab (bs, es->s_frags_per_group);
+	sb->u.ext2_sb.s_inodes_per_group = e_swab (bs, es->s_inodes_per_group);
 	sb->u.ext2_sb.s_inodes_per_block = sb->s_blocksize /
 					   EXT2_INODE_SIZE(sb);
 	sb->u.ext2_sb.s_itb_per_group = sb->u.ext2_sb.s_inodes_per_group /
@@ -483,12 +499,12 @@
 	if (resuid != EXT2_DEF_RESUID)
 		sb->u.ext2_sb.s_resuid = resuid;
 	else
-		sb->u.ext2_sb.s_resuid = es->s_def_resuid;
+		sb->u.ext2_sb.s_resuid = e_swab (bs, es->s_def_resuid);
 	if (resgid != EXT2_DEF_RESGID)
 		sb->u.ext2_sb.s_resgid = resgid;
 	else
-		sb->u.ext2_sb.s_resgid = es->s_def_resgid;
-	sb->u.ext2_sb.s_mount_state = es->s_state;
+		sb->u.ext2_sb.s_resgid = e_swab (bs, es->s_def_resgid);
+	sb->u.ext2_sb.s_mount_state = e_swab (bs, es->s_state);
 	sb->u.ext2_sb.s_rename_lock = 0;
 	sb->u.ext2_sb.s_rename_wait = NULL;
 	sb->u.ext2_sb.s_addr_per_block_bits =
@@ -531,8 +547,8 @@
 		goto failed_mount;
 	}
 
-	sb->u.ext2_sb.s_groups_count = (es->s_blocks_count -
-				        es->s_first_data_block +
+	sb->u.ext2_sb.s_groups_count = (e_swab (bs, es->s_blocks_count) -
+				        e_swab (bs, es->s_first_data_block) +
 				       EXT2_BLOCKS_PER_GROUP(sb) - 1) /
 				       EXT2_BLOCKS_PER_GROUP(sb);
 	db_count = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
@@ -596,7 +612,7 @@
 static void ext2_commit_super (struct super_block * sb,
 			       struct ext2_super_block * es)
 {
-	es->s_wtime = CURRENT_TIME;
+	e_set_swab (BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped), es->s_wtime, CURRENT_TIME);
 	mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 	sb->s_dirt = 0;
 }
@@ -615,15 +631,17 @@
 void ext2_write_super (struct super_block * sb)
 {
 	struct ext2_super_block * es;
+	int bs;
 
 	if (!(sb->s_flags & MS_RDONLY)) {
 		es = sb->u.ext2_sb.s_es;
+		bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 
 		ext2_debug ("setting valid to 0\n");
 
-		if (es->s_state & EXT2_VALID_FS) {
-			es->s_state &= ~EXT2_VALID_FS;
-			es->s_mtime = CURRENT_TIME;
+		if (e_swab (bs, es->s_state) & EXT2_VALID_FS) {
+			es->s_state &= ~(bs ? EXT2_VALID_FS << 8 : EXT2_VALID_FS);
+			e_set_swab (bs, es->s_mtime, CURRENT_TIME);
 		}
 		ext2_commit_super (sb, es);
 	}
@@ -637,6 +655,7 @@
 	unsigned short resgid = sb->u.ext2_sb.s_resgid;
 	unsigned long new_mount_opt;
 	unsigned long tmp;
+	int bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 
 	/*
 	 * Allow the "check" option to be passed as a remount option.
@@ -653,15 +672,15 @@
 	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
 		return 0;
 	if (*flags & MS_RDONLY) {
-		if (es->s_state & EXT2_VALID_FS ||
+		if (e_swab (bs, es->s_state) & EXT2_VALID_FS ||
 		    !(sb->u.ext2_sb.s_mount_state & EXT2_VALID_FS))
 			return 0;
 		/*
 		 * OK, we are remounting a valid rw partition rdonly, so set
 		 * the rdonly flag and then mark the partition as valid again.
 		 */
-		es->s_state = sb->u.ext2_sb.s_mount_state;
-		es->s_mtime = CURRENT_TIME;
+		e_set_swab (bs, es->s_state, sb->u.ext2_sb.s_mount_state);
+		e_set_swab (bs, es->s_mtime, CURRENT_TIME);
 		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 		sb->s_dirt = 1;
 		ext2_commit_super (sb, es);
@@ -672,7 +691,7 @@
 		 * store the current valid flag.  (It may have been changed 
 		 * by e2fsck since we originally mounted the partition.)
 		 */
-		sb->u.ext2_sb.s_mount_state = es->s_state;
+		sb->u.ext2_sb.s_mount_state = e_swab (bs, es->s_state);
 		sb->s_flags &= ~MS_RDONLY;
 		ext2_setup_super (sb, es);
 	}
@@ -710,6 +729,7 @@
 	unsigned long overhead;
 	unsigned long overhead_per_group;
 	struct statfs tmp;
+	int bs = BYTE_SWAP(sb->u.ext2_sb.s_byte_swapped);
 
 	if (test_opt (sb, MINIX_DF))
 		overhead = 0;
@@ -722,18 +742,18 @@
 				     1 /* block bitmap */ +
 				     1 /* inode bitmap */ +
 				     sb->u.ext2_sb.s_itb_per_group /* inode table */;
-		overhead = sb->u.ext2_sb.s_es->s_first_data_block +
+		overhead = e_swab (bs, sb->u.ext2_sb.s_es->s_first_data_block) +
 			   sb->u.ext2_sb.s_groups_count * overhead_per_group;
 	}
 
 	tmp.f_type = EXT2_SUPER_MAGIC;
 	tmp.f_bsize = sb->s_blocksize;
-	tmp.f_blocks = sb->u.ext2_sb.s_es->s_blocks_count - overhead;
+	tmp.f_blocks = e_swab (bs, sb->u.ext2_sb.s_es->s_blocks_count) - overhead;
 	tmp.f_bfree = ext2_count_free_blocks (sb);
-	tmp.f_bavail = tmp.f_bfree - sb->u.ext2_sb.s_es->s_r_blocks_count;
-	if (tmp.f_bfree < sb->u.ext2_sb.s_es->s_r_blocks_count)
+	tmp.f_bavail = tmp.f_bfree - e_swab (bs, sb->u.ext2_sb.s_es->s_r_blocks_count);
+	if (tmp.f_bfree < e_swab (bs, sb->u.ext2_sb.s_es->s_r_blocks_count))
 		tmp.f_bavail = 0;
-	tmp.f_files = sb->u.ext2_sb.s_es->s_inodes_count;
+	tmp.f_files = e_swab (bs, sb->u.ext2_sb.s_es->s_inodes_count);
 	tmp.f_ffree = ext2_count_free_inodes (sb);
 	tmp.f_namelen = EXT2_NAME_LEN;
 	memcpy_tofs(buf, &tmp, bufsiz);
diff -urN pc-2.0.21/fs/ext2/truncate.c linux-2.0.21/fs/ext2/truncate.c
--- pc-2.0.21/fs/ext2/truncate.c	Mon Dec 11 05:56:35 1995
+++ linux-2.0.21/fs/ext2/truncate.c	Fri Aug 30 13:30:18 1996
@@ -107,12 +107,12 @@
 	return retry;
 }
 
-static int trunc_indirect (struct inode * inode, int offset, u32 * p)
+static int trunc_indirect (struct inode * inode, int offset, u32 * p, int bs1)
 {
-	int i, tmp;
+	int i;
 	struct buffer_head * bh;
 	struct buffer_head * ind_bh;
-	u32 * ind;
+	u32 * ind, tmp;
 	unsigned long block_to_free = 0;
 	unsigned long free_count = 0;
 	int retry = 0;
@@ -120,11 +120,12 @@
 	int blocks = inode->i_sb->s_blocksize / 512;
 #define INDIRECT_BLOCK ((int)DIRECT_BLOCK - offset)
 	int indirect_block = INDIRECT_BLOCK;
+	int bs = BYTE_SWAP(inode->i_sb->u.ext2_sb.s_byte_swapped);
 
 	tmp = *p;
 	if (!tmp)
 		return 0;
-	ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+	ind_bh = bread (inode->i_dev, e_swab (bs1, tmp), inode->i_sb->s_blocksize);
 	if (tmp != *p) {
 		brelse (ind_bh);
 		return 1;
@@ -143,7 +144,7 @@
 		tmp = *ind;
 		if (!tmp)
 			continue;
-		bh = get_hash_table (inode->i_dev, tmp,
+		bh = get_hash_table (inode->i_dev, e_swab (bs, tmp),
 				     inode->i_sb->s_blocksize);
 		if (i < indirect_block) {
 			brelse (bh);
@@ -157,6 +158,7 @@
 		*ind = 0;
 		mark_buffer_dirty(ind_bh, 1);
 		bforget(bh);
+		tmp = e_swab (bs, tmp);
 		if (free_count == 0) {
 			block_to_free = tmp;
 			free_count++;
@@ -185,7 +187,7 @@
 			*p = 0;
 			inode->i_blocks -= blocks;
 			inode->i_dirt = 1;
-			ext2_free_blocks (inode, tmp, 1);
+			ext2_free_blocks (inode, e_swab (bs1, tmp), 1);
 		}
 	if (IS_SYNC(inode) && buffer_dirty(ind_bh)) {
 		ll_rw_block (WRITE, 1, &ind_bh);
@@ -196,21 +198,22 @@
 }
 
 static int trunc_dindirect (struct inode * inode, int offset,
-			    u32 * p)
+			    u32 * p, int bs1)
 {
-	int i, tmp;
+	int i;
 	struct buffer_head * dind_bh;
-	u32 * dind;
+	u32 * dind, tmp;
 	int retry = 0;
 	int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
 	int blocks = inode->i_sb->s_blocksize / 512;
 #define DINDIRECT_BLOCK (((int)DIRECT_BLOCK - offset) / addr_per_block)
 	int dindirect_block = DINDIRECT_BLOCK;
+	int bs = BYTE_SWAP(inode->i_sb->u.ext2_sb.s_byte_swapped);
 
 	tmp = *p;
 	if (!tmp)
 		return 0;
-	dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+	dind_bh = bread (inode->i_dev, e_swab (bs1, tmp), inode->i_sb->s_blocksize);
 	if (tmp != *p) {
 		brelse (dind_bh);
 		return 1;
@@ -230,7 +233,7 @@
 		if (!tmp)
 			continue;
 		retry |= trunc_indirect (inode, offset + (i * addr_per_block),
-					  dind);
+					  dind, bs);
 		mark_buffer_dirty(dind_bh, 1);
 	}
 	dind = (u32 *) dind_bh->b_data;
@@ -245,7 +248,7 @@
 			*p = 0;
 			inode->i_blocks -= blocks;
 			inode->i_dirt = 1;
-			ext2_free_blocks (inode, tmp, 1);
+			ext2_free_blocks (inode, e_swab (bs1, tmp), 1);
 		}
 	if (IS_SYNC(inode) && buffer_dirty(dind_bh)) {
 		ll_rw_block (WRITE, 1, &dind_bh);
@@ -257,9 +260,9 @@
 
 static int trunc_tindirect (struct inode * inode)
 {
-	int i, tmp;
+	int i;
 	struct buffer_head * tind_bh;
-	u32 * tind, * p;
+	u32 * tind, * p, tmp;
 	int retry = 0;
 	int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
 	int blocks = inode->i_sb->s_blocksize / 512;
@@ -267,6 +270,7 @@
 			  addr_per_block + EXT2_NDIR_BLOCKS)) / \
 			  (addr_per_block * addr_per_block))
 	int tindirect_block = TINDIRECT_BLOCK;
+	int bs = BYTE_SWAP(inode->i_sb->u.ext2_sb.s_byte_swapped);
 
 	p = inode->u.ext2_i.i_data + EXT2_TIND_BLOCK;
 	if (!(tmp = *p))
@@ -289,7 +293,7 @@
 		tind = i + (u32 *) tind_bh->b_data;
 		retry |= trunc_dindirect(inode, EXT2_NDIR_BLOCKS +
 			addr_per_block + (i + 1) * addr_per_block * addr_per_block,
-			tind);
+			tind, bs);
 		mark_buffer_dirty(tind_bh, 1);
 	}
 	tind = (u32 *) tind_bh->b_data;
@@ -330,10 +334,10 @@
 	while (1) {
 		retry = trunc_direct(inode);
 		retry |= trunc_indirect (inode, EXT2_IND_BLOCK,
-			(u32 *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK]);
+			(u32 *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK], 0);
 		retry |= trunc_dindirect (inode, EXT2_IND_BLOCK +
 			EXT2_ADDR_PER_BLOCK(inode->i_sb),
-			(u32 *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK]);
+			(u32 *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK], 0);
 		retry |= trunc_tindirect (inode);
 		if (!retry)
 			break;
diff -urN pc-2.0.21/fs/fat/cache.c linux-2.0.21/fs/fat/cache.c
--- pc-2.0.21/fs/fat/cache.c	Fri May 10 06:54:52 1996
+++ linux-2.0.21/fs/fat/cache.c	Sat Aug 24 14:41:16 1996
@@ -4,6 +4,7 @@
  *  Written 1992,1993 by Werner Almesberger
  */
 
+#include <linux/config.h>
 #include <linux/msdos_fs.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -277,7 +278,18 @@
 	}
 	if (last)
 		fat_access(inode->i_sb,last,MSDOS_SB(inode->i_sb)->fat_bits ==
+			   /*
+			    * For Atari: The GEMDOS uses 0xffff for a
+			    * end-of-file and I've seen programs that can't
+			    * deal with other values. I don't even know if
+			    * GEMDOS itself could handle EOF's != 0xffff
+			    * correctly in all cases. So better use 0xffff.
+			    */
+#ifdef CONFIG_ATARI
+		    12 ? 0xfff : 0xffff);
+#else
 		    12 ? 0xff8 : 0xfff8);
+#endif
 	else {
 		MSDOS_I(inode)->i_start = 0;
 		inode->i_dirt = 1;
diff -urN pc-2.0.21/fs/fat/inode.c linux-2.0.21/fs/fat/inode.c
--- pc-2.0.21/fs/fat/inode.c	Sun Aug  4 12:39:07 1996
+++ linux-2.0.21/fs/fat/inode.c	Sat Aug 24 14:41:21 1996
@@ -8,6 +8,7 @@
 #define __NO_VERSION__
 #include <linux/module.h>
 
+#include <linux/config.h>
 #include <linux/msdos_fs.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -266,14 +267,35 @@
 	if (!error) {
 		MSDOS_SB(sb)->clusters = b->cluster_size ? data_sectors/
 		    b->cluster_size/sector_mult : 0;
+#ifndef CONFIG_ATARI
 		MSDOS_SB(sb)->fat_bits = fat ? fat : MSDOS_SB(sb)->clusters >
 		    MSDOS_FAT12 ? 16 : 12;
+#else
+		/* Atari GEMDOS partitions always have 16-bit fat */
+		MSDOS_SB(sb)->fat_bits = fat ? fat : 16;
+		/* If more clusters than fat entries in 16-bit fat, we assume
+		 * it's a real MSDOS partition with 12-bit fat.
+		 */
+		if (MSDOS_SB(sb)->clusters+2 > MSDOS_SB(sb)->
+		    fat_length*SECTOR_SIZE*8/MSDOS_SB(sb)->fat_bits)
+			MSDOS_SB(sb)->fat_bits = 12;
+		/* if its a floppy disk --> 12bit fat */
+		if (MAJOR(sb->s_dev) == FLOPPY_MAJOR)
+			MSDOS_SB(sb)->fat_bits = 12;
+#endif
 		fat_clusters = MSDOS_SB(sb)->fat_length*SECTOR_SIZE*8/
 		    MSDOS_SB(sb)->fat_bits;
 		error = !MSDOS_SB(sb)->fats || (MSDOS_SB(sb)->dir_entries &
 		    (MSDOS_DPS-1)) || MSDOS_SB(sb)->clusters+2 > fat_clusters+
 		    MSDOS_MAX_EXTRA || (logical_sector_size & (SECTOR_SIZE-1))
+#ifndef CONFIG_ATARI
 		    || !b->secs_track || !b->heads;
+#else
+		    /* secs_track and heads may be arbitrary on GEMDOS
+		       partitions, it depends on partitioning software
+		       used.  */
+		    /*|| !b->secs_track || !b->heads */;
+#endif
 	}
 	fat_brelse(sb, bh);
 	/*
diff -urN pc-2.0.21/fs/fat/misc.c linux-2.0.21/fs/fat/misc.c
--- pc-2.0.21/fs/fat/misc.c	Fri May 10 06:54:52 1996
+++ linux-2.0.21/fs/fat/misc.c	Sat Aug 24 14:41:22 1996
@@ -4,6 +4,7 @@
  *  Written 1992,1993 by Werner Almesberger
  */
 
+#include <linux/config.h>
 #include <linux/fs.h>
 #include <linux/msdos_fs.h>
 #include <linux/sched.h>
@@ -140,7 +141,18 @@
 		return -ENOSPC;
 	}
 	fat_access(sb,nr,MSDOS_SB(sb)->fat_bits == 12 ?
+		   /*
+		    * For Atari: The GEMDOS uses 0xffff for a
+		    * end-of-file and I've seen programs that can't
+		    * deal with other values. I don't even know if
+		    * GEMDOS itself could handle EOF's != 0xffff
+		    * correctly in all cases. So better use 0xffff.
+		    */
+#ifdef CONFIG_ATARI
+	    0xfff : 0xffff);
+#else
 	    0xff8 : 0xfff8);
+#endif
 	if (MSDOS_SB(sb)->free_clusters != -1)
 		MSDOS_SB(sb)->free_clusters--;
 	unlock_fat(sb);
diff -urN pc-2.0.21/fs/minix/bitmap.c linux-2.0.21/fs/minix/bitmap.c
--- pc-2.0.21/fs/minix/bitmap.c	Tue Mar 12 06:54:11 1996
+++ linux-2.0.21/fs/minix/bitmap.c	Sat Aug 24 14:41:54 1996
@@ -4,6 +4,11 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
+/*
+ * Modified for 680x0 by Hamish Macdonald
+ * Fixed for 680x0 by Andreas Schwab
+ */
+
 /* bitmap.c contains the code that handles the inode and block bitmaps */
 
 #include <linux/sched.h>
@@ -57,7 +62,7 @@
 		printk("minix_free_block: nonexistent bitmap buffer\n");
 		return;
 	}
-	if (!clear_bit(bit,bh->b_data))
+	if (!minix_clear_bit(bit,bh->b_data))
 		printk("free_block (%s:%d): bit already cleared\n",
 		       kdevname(sb->s_dev), block);
 	mark_buffer_dirty(bh, 1);
@@ -77,11 +82,11 @@
 	j = 8192;
 	for (i=0 ; i<64 ; i++)
 		if ((bh=sb->u.minix_sb.s_zmap[i]) != NULL)
-			if ((j=find_first_zero_bit(bh->b_data, 8192)) < 8192)
+			if ((j=minix_find_first_zero_bit(bh->b_data, 8192)) < 8192)
 				break;
 	if (i>=64 || !bh || j>=8192)
 		return 0;
-	if (set_bit(j,bh->b_data)) {
+	if (minix_set_bit(j,bh->b_data)) {
 		printk("new_block: bit already set");
 		goto repeat;
 	}
@@ -209,7 +214,7 @@
 	}
 	minix_clear_inode(inode);
 	clear_inode(inode);
-	if (!clear_bit(ino & 8191, bh->b_data))
+	if (!minix_clear_bit(ino & 8191, bh->b_data))
 		printk("free_inode: bit %lu already cleared.\n",ino);
 	mark_buffer_dirty(bh, 1);
 }
@@ -229,13 +234,13 @@
 	j = 8192;
 	for (i=0 ; i<8 ; i++)
 		if ((bh = inode->i_sb->u.minix_sb.s_imap[i]) != NULL)
-			if ((j=find_first_zero_bit(bh->b_data, 8192)) < 8192)
+			if ((j=minix_find_first_zero_bit(bh->b_data, 8192)) < 8192)
 				break;
 	if (!bh || j >= 8192) {
 		iput(inode);
 		return NULL;
 	}
-	if (set_bit(j,bh->b_data)) {	/* shouldn't happen */
+	if (minix_set_bit(j,bh->b_data)) {	/* shouldn't happen */
 		printk("new_inode: bit already set");
 		iput(inode);
 		return NULL;
diff -urN pc-2.0.21/fs/minix/inode.c linux-2.0.21/fs/minix/inode.c
--- pc-2.0.21/fs/minix/inode.c	Fri Apr 19 14:07:22 1996
+++ linux-2.0.21/fs/minix/inode.c	Sat Aug 24 14:41:56 1996
@@ -5,6 +5,8 @@
  *
  *  Copyright (C) 1996  Gertjan van Wingerde    (gertjan@cs.vu.nl)
  *	Minix V2 fs support.
+ *
+ *  Modified for 680x0 by Andreas Schwab
  */
 
 #include <linux/module.h>
@@ -263,8 +265,8 @@
 		MOD_DEC_USE_COUNT;
 		return NULL;
 	}
-	set_bit(0,s->u.minix_sb.s_imap[0]->b_data);
-	set_bit(0,s->u.minix_sb.s_zmap[0]->b_data);
+	minix_set_bit(0,s->u.minix_sb.s_imap[0]->b_data);
+	minix_set_bit(0,s->u.minix_sb.s_zmap[0]->b_data);
 	unlock_super(s);
 	/* set up enough so that it can read an inode */
 	s->s_dev = dev;
diff -urN pc-2.0.21/fs/msdos/namei.c linux-2.0.21/fs/msdos/namei.c
--- pc-2.0.21/fs/msdos/namei.c	Wed Jul  3 11:08:03 1996
+++ linux-2.0.21/fs/msdos/namei.c	Wed Sep 25 22:29:44 1996
@@ -5,6 +5,8 @@
  *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
  */
 
+#include <linux/config.h>
+
 #define __NO_VERSION__
 #include <linux/module.h>
 
@@ -25,17 +27,23 @@
 /* MS-DOS "device special files" */
 
 static const char *reserved_names[] = {
+#ifndef CONFIG_ATARI /* GEMDOS is less stupid */
     "CON     ","PRN     ","NUL     ","AUX     ",
     "LPT1    ","LPT2    ","LPT3    ","LPT4    ",
     "COM1    ","COM2    ","COM3    ","COM4    ",
+#endif
     NULL };
 
 
 /* Characters that are undesirable in an MS-DOS file name */
   
 static char bad_chars[] = "*?<>|\"";
+#ifdef CONFIG_ATARI
+/* GEMDOS is less restrictive */
+static char bad_if_strict[] = " ";
+#else
 static char bad_if_strict[] = "+=,; ";
-
+#endif
 
 void msdos_put_super(struct super_block *sb)
 {
@@ -96,7 +104,11 @@
 		/* Get rid of dot - test for it elsewhere */
 		name++; len--;
 	}
+#ifndef CONFIG_ATARI
 	space = 1; /* disallow names that _really_ start with a dot */
+#else
+	space = 0; /* GEMDOS does not care */
+#endif
 	c = 0;
 	for (walk = res; len && walk-res < 8; walk++) {
 	    	c = *name++;
@@ -129,8 +141,13 @@
 			if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
 			if (conv == 's' && strchr(bad_if_strict,c))
 				return -EINVAL;
-			if (c < ' ' || c == ':' || c == '\\' || c == '.')
+			if (c < ' ' || c == ':' || c == '\\')
 				return -EINVAL;
+			if (c == '.') {
+				if (conv == 's')
+					return -EINVAL;
+				break;
+			}
 			if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL;
 			space = c == ' ';
 			*walk++ = c >= 'a' && c <= 'z' ? c-32 : c;
diff -urN pc-2.0.21/fs/proc/array.c linux-2.0.21/fs/proc/array.c
--- pc-2.0.21/fs/proc/array.c	Wed Aug 21 08:12:49 1996
+++ linux-2.0.21/fs/proc/array.c	Mon Sep 16 23:54:06 1996
@@ -50,6 +50,7 @@
 #include <asm/segment.h>
 #include <asm/pgtable.h>
 #include <asm/io.h>
+#include <asm/shmparam.h>
 
 #define LOAD_INT(x) ((x) >> FSHIFT)
 #define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
@@ -66,7 +67,7 @@
 	int count1;
 	char * pnt;
 	struct user dump;
-#ifdef __i386__
+#if defined (__i386__) || defined (__mc68000__)
 #	define FIRST_MAPPED	PAGE_SIZE	/* we don't have page 0 mapped on x86.. */
 #else
 #	define FIRST_MAPPED	0
@@ -454,6 +455,30 @@
 	    }
 	    return pc;
 	}
+#elif defined(__mc68000__)
+	{
+	    unsigned long fp, pc;
+	    unsigned long stack_page;
+	    int count = 0;
+	    extern int sys_pause (void);
+
+	    stack_page = p->kernel_stack_page;
+	    if (!stack_page)
+		    return 0;
+	    fp = ((struct switch_stack *)p->tss.ksp)->a6;
+	    do {
+		    if (fp < stack_page || fp >= 4088+stack_page)
+			    return 0;
+		    pc = ((unsigned long *)fp)[1];
+		/* FIXME: This depends on the order of these functions. */
+		    if ((pc < (unsigned long) __down
+		         || pc >= (unsigned long) add_timer)
+		        && (pc < (unsigned long) schedule
+			    || pc >= (unsigned long) sys_pause))
+		      return pc;
+		    fp = *(unsigned long *) fp;
+	    } while (count++ < 16);
+	}
 #endif
 	return 0;
 }
@@ -469,12 +494,20 @@
 				 + (long)&((struct pt_regs *)0)->reg)
 # define KSTK_EIP(tsk)	(*(unsigned long *)(tsk->kernel_stack_page + PT_REG(pc)))
 # define KSTK_ESP(tsk)	((tsk) == current ? rdusp() : (tsk)->tss.usp)
+#elif defined(__mc68000__)
+#define	KSTK_EIP(tsk)	\
+    ({			\
+	unsigned long eip = 0;	 \
+ 	if ((tsk)->tss.esp0 > PAGE_SIZE && (tsk)->tss.esp0 < high_memory) \
+	      eip = ((struct pt_regs *) (tsk)->tss.esp0)->pc;	 \
+        eip; })
+#define	KSTK_ESP(tsk)	((tsk) == current ? rdusp() : (tsk)->tss.usp)
 #elif defined(__sparc__)
 # define PT_REG(reg)            (PAGE_SIZE - sizeof(struct pt_regs)     \
                                  + (long)&((struct pt_regs *)0)->reg)
 # define KSTK_EIP(tsk)  (*(unsigned long *)(tsk->kernel_stack_page + PT_REG(pc)))
 # define KSTK_ESP(tsk)  (*(unsigned long *)(tsk->kernel_stack_page + PT_REG(u_regs[UREG_FP])))
-#endif
+#endif /* arch */
 
 /* Gcc optimizes away "strlen(x)" for constant x */
 #define ADDBUF(buffer, string) \
@@ -827,7 +860,7 @@
 				trs += pages;	/* text */
 			else if (vma->vm_flags & VM_GROWSDOWN)
 				drs += pages;	/* stack */
-			else if (vma->vm_end > 0x60000000)
+			else if (vma->vm_end > SHM_RANGE_START)
 				lrs += pages;	/* library */
 			else
 				drs += pages;
@@ -979,6 +1012,12 @@
 #ifdef __SMP_PROF__
 extern int get_smp_prof_list(char *);
 #endif
+#ifdef __mc68000__
+extern int get_hardware_list(char *);
+#ifdef CONFIG_ZORRO
+extern int zorro_get_list(char *);
+#endif /* CONFIG_ZORRO */
+#endif /* __mc68000__ */
 
 static int get_root_array(char * page, int type, char **start, off_t offset, int length)
 {
@@ -1052,6 +1091,14 @@
 #endif
 		case PROC_LOCKS:
 			return get_locks_status(page);
+#ifdef __mc68000__
+		case PROC_HARDWARE:
+			return get_hardware_list(page);
+#ifdef CONFIG_ZORRO
+		case PROC_ZORRO:
+			return zorro_get_list(page);
+#endif /* CONFIG_ZORRO */
+#endif /* __mc68000__ */
 	}
 	return -EBADF;
 }
diff -urN pc-2.0.21/fs/proc/root.c linux-2.0.21/fs/proc/root.c
--- pc-2.0.21/fs/proc/root.c	Tue Apr 30 12:09:45 1996
+++ linux-2.0.21/fs/proc/root.c	Sat Aug 24 14:42:39 1996
@@ -362,6 +362,18 @@
 		PROC_LOCKS, 5, "locks",
 		S_IFREG | S_IRUGO, 1, 0, 0,
 	});
+#ifdef __mc68000__
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_HARDWARE, 8, "hardware",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+#ifdef CONFIG_ZORRO
+	proc_register(&proc_root, &(struct proc_dir_entry) {
+		PROC_ZORRO, 5, "zorro",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+	});
+#endif /* CONFIG_ZORRO */
+#endif /* __mc68000__ */
 
 	proc_register( &proc_root, &(struct proc_dir_entry)
 	   { PROC_MTAB, 6, "mounts", S_IFREG | S_IRUGO, 1, 0, 0, } );
diff -urN pc-2.0.21/fs/umsdos/mangle.c linux-2.0.21/fs/umsdos/mangle.c
--- pc-2.0.21/fs/umsdos/mangle.c	Sun Feb 25 10:17:59 1996
+++ linux-2.0.21/fs/umsdos/mangle.c	Sat Aug 24 14:43:11 1996
@@ -64,12 +64,7 @@
 			 ever think of using such a name in real life. This is not
 			 fool proof. I don't think there is a total solution to this.
 		*/
-		union {
-			int entry_num;
-			struct {
-				unsigned num1:5,num2:5,num3:5;
-			}num;
-		} u;
+		int entry_num;
 		char *pt = info->fake.fname + info->fake.len;
 		/* lookup for encoding the last character of the extension */
 		/* It contain valid character after the ugly one to make sure */
@@ -84,16 +79,16 @@
 			'p','q','r','s','t','u','v' 
 		};
 		#define lookup12 (lookup3+9)
-		u.entry_num = info->f_pos / UMSDOS_REC_SIZE;
-		if (u.entry_num > (9* 32 * 32)){
+		entry_num = info->f_pos / UMSDOS_REC_SIZE;
+		if (entry_num > (9* 32 * 32)){
 			printk ("UMSDOS: More than 9216 file in a directory.\n"
 				"This may break the mangling strategy.\n"
 				"Not a killer problem. See doc.\n");
 		}
 		*pt++ = '.';
-		*pt++ = lookup3 [u.num.num3];
-		*pt++ = lookup12[u.num.num2];
-		*pt++ = lookup12[u.num.num1];
+		*pt++ = lookup3 [(entry_num >> 10) & 31];
+		*pt++ = lookup12[(entry_num >> 5) & 31];
+		*pt++ = lookup12[entry_num & 31];
 		*pt = '\0';		/* help doing printk */	
 		info->fake.len += 4;
 		info->msdos_reject = 0;		/* Avoid mangling twice */
@@ -171,7 +166,7 @@
 				like this.
 			*/
 			int i;
-			static const char *spc = "\"*+,/:;<=>?[\\]|~";
+			const char *spc = "\"*+,/:;<=>?[\\]|~";
 			is_init = 1;
 			for (i=0; i<=32; i++) lkp[i] = '#';
 			for (i=33; i<'A'; i++) lkp[i] = (char)i;
diff -urN pc-2.0.21/include/asm-alpha/bitops.h linux-2.0.21/include/asm-alpha/bitops.h
--- pc-2.0.21/include/asm-alpha/bitops.h	Wed May 29 17:47:38 1996
+++ linux-2.0.21/include/asm-alpha/bitops.h	Sat Aug 24 14:44:05 1996
@@ -160,4 +160,18 @@
 #define find_first_zero_bit(addr, size) \
 	find_next_zero_bit((addr), (size), 0)
 
+/* Bitmap functions for the minix filesystem.  */
+#define minix_set_bit(nr,addr) set_bit(nr,addr)
+#define minix_clear_bit(nr,addr) clear_bit(nr,addr)
+#define minix_test_bit(nr,addr) test_bit(nr,addr)
+#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
+
+/* Bitmap functions for the ext2 filesystem. */
+#define ext2_set_bit(nr,addr) set_bit(nr,addr)
+#define ext2_clear_bit(nr,addr) clear_bit(nr,addr)
+#define ext2_test_bit(nr,addr) test_bit(nr,addr)
+#define ext2_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
+#define ext2_find_next_zero_bit(addr,size,offset) \
+	find_next_zero_bit(addr,size,offset)
+
 #endif /* _ALPHA_BITOPS_H */
diff -urN pc-2.0.21/include/asm-alpha/setup.h linux-2.0.21/include/asm-alpha/setup.h
--- pc-2.0.21/include/asm-alpha/setup.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/include/asm-alpha/setup.h	Sat Aug 24 14:44:23 1996
@@ -0,0 +1,4 @@
+#ifndef _ALPHA_SETUP_H
+#define _ALPHA_SETUP_H
+
+#endif /* _ALPHA_SETUP_H */
diff -urN pc-2.0.21/include/asm-i386/bitops.h linux-2.0.21/include/asm-i386/bitops.h
--- pc-2.0.21/include/asm-i386/bitops.h	Thu Apr 11 12:22:13 1996
+++ linux-2.0.21/include/asm-i386/bitops.h	Sat Aug 24 14:44:35 1996
@@ -134,4 +134,18 @@
 	return word;
 }
 
+/* Bitmap functions for the minix filesystem.  */
+#define minix_set_bit(nr,addr) set_bit(nr,addr)
+#define minix_clear_bit(nr,addr) clear_bit(nr,addr)
+#define minix_test_bit(nr,addr) test_bit(nr,addr)
+#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
+
+/* Bitmap functions for the ext2 filesystem. */
+#define ext2_set_bit(nr,addr) set_bit(nr,addr)
+#define ext2_clear_bit(nr,addr) clear_bit(nr,addr)
+#define ext2_test_bit(nr,addr) test_bit(nr,addr)
+#define ext2_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
+#define ext2_find_next_zero_bit(addr,size,offset) \
+	find_next_zero_bit(addr,size,offset)
+
 #endif /* _I386_BITOPS_H */
diff -urN pc-2.0.21/include/asm-i386/ide.h linux-2.0.21/include/asm-i386/ide.h
--- pc-2.0.21/include/asm-i386/ide.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/include/asm-i386/ide.h	Sat Aug 24 14:44:41 1996
@@ -0,0 +1,14 @@
+#ifndef _I386_IDE_H
+#define _I386_IDE_H
+
+typedef int ide_ioreg_t;
+
+#define IDE_IO_PORT_IO
+
+#ifndef MAX_HWIFS
+#define MAX_HWIFS	4	/* an arbitrary, but realistic limit */
+#endif
+
+#define STI()   sti()
+
+#endif /* _I386_IDE_H */
diff -urN pc-2.0.21/include/asm-i386/pgtable.h linux-2.0.21/include/asm-i386/pgtable.h
--- pc-2.0.21/include/asm-i386/pgtable.h	Fri Sep 20 15:23:31 1996
+++ linux-2.0.21/include/asm-i386/pgtable.h	Sun Sep  8 14:16:18 1996
@@ -25,6 +25,7 @@
 #define flush_cache_range(mm, start, end)	do { } while (0)
 #define flush_cache_page(vma, vmaddr)		do { } while (0)
 #define flush_page_to_ram(page)			do { } while (0)
+#define flush_pages_to_ram(page)		do { } while (0)
 
 /*
  * TLB flushing:
diff -urN pc-2.0.21/include/asm-i386/setup.h linux-2.0.21/include/asm-i386/setup.h
--- pc-2.0.21/include/asm-i386/setup.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/include/asm-i386/setup.h	Sat Aug 24 14:44:50 1996
@@ -0,0 +1,4 @@
+#ifndef _I386_SETUP_H
+#define _I386_SETUP_H
+
+#endif /* _I386_SETUP_H */
diff -urN pc-2.0.21/include/asm-m68k/amigahw.h linux-2.0.21/include/asm-m68k/amigahw.h
--- pc-2.0.21/include/asm-m68k/amigahw.h	Mon May 20 06:54:28 1996
+++ linux-2.0.21/include/asm-m68k/amigahw.h	Sat Aug 24 14:45:02 1996
@@ -195,6 +195,7 @@
 void amiga_chip_init (void);
 void *amiga_chip_alloc (long size);
 void amiga_chip_free (void *);
+unsigned long amiga_chip_avail( void ); /*MILAN*/
 
 struct tod3000 {
   unsigned int  :28, second2:4;	/* lower digit */
diff -urN pc-2.0.21/include/asm-m68k/amigaints.h linux-2.0.21/include/asm-m68k/amigaints.h
--- pc-2.0.21/include/asm-m68k/amigaints.h	Mon May 20 06:54:28 1996
+++ linux-2.0.21/include/asm-m68k/amigaints.h	Sat Aug 24 14:45:03 1996
@@ -18,7 +18,9 @@
 **
 */
 
-#define NUM_AMIGA_SOURCES   (24)
+#define AMI_IRQS            (24)
+#define AMI_STD_IRQS        (14)
+#define CIA_IRQS            (5)
 
 /* vertical blanking interrupt */
 #define IRQ_AMIGA_VERTB     (IRQ_MACHSPEC | 0)
@@ -43,22 +45,26 @@
 #define IRQ_AMIGA_RBF	    (IRQ_MACHSPEC | 9)
 #define IRQ_AMIGA_TBE	    (IRQ_MACHSPEC | 10)
 
-/* CIA interrupt sources */
-#define IRQ_AMIGA_CIAA_TA   (IRQ_MACHSPEC | 11)
-#define IRQ_AMIGA_CIAA_TB   (IRQ_MACHSPEC | 12)
-#define IRQ_AMIGA_CIAA_ALRM (IRQ_MACHSPEC | 13)
-#define IRQ_AMIGA_CIAA_SP   (IRQ_MACHSPEC | 14)
-#define IRQ_AMIGA_CIAA_FLG  (IRQ_MACHSPEC | 15)
-#define IRQ_AMIGA_CIAB_TA   (IRQ_MACHSPEC | 16)
-#define IRQ_AMIGA_CIAB_TB   (IRQ_MACHSPEC | 17)
-#define IRQ_AMIGA_CIAB_ALRM (IRQ_MACHSPEC | 18)
-#define IRQ_AMIGA_CIAB_SP   (IRQ_MACHSPEC | 19)
-#define IRQ_AMIGA_CIAB_FLG  (IRQ_MACHSPEC | 20)
+/* software interrupts */
+#define IRQ_AMIGA_SOFT      (IRQ_MACHSPEC | 11)
 
-#define IRQ_AMIGA_SOFT      (IRQ_MACHSPEC | 21)
+/* interrupts from external hardware */
+#define IRQ_AMIGA_PORTS	    (IRQ_MACHSPEC | 12)
+#define IRQ_AMIGA_EXTER	    (IRQ_MACHSPEC | 13)
 
-#define IRQ_AMIGA_PORTS	    (IRQ_MACHSPEC | 22)
-#define IRQ_AMIGA_EXTER	    (IRQ_MACHSPEC | 23)
+/* CIA interrupt sources */
+#define IRQ_AMIGA_CIAA      (IRQ_MACHSPEC | 14)
+#define IRQ_AMIGA_CIAA_TA   (IRQ_MACHSPEC | 14)
+#define IRQ_AMIGA_CIAA_TB   (IRQ_MACHSPEC | 15)
+#define IRQ_AMIGA_CIAA_ALRM (IRQ_MACHSPEC | 16)
+#define IRQ_AMIGA_CIAA_SP   (IRQ_MACHSPEC | 17)
+#define IRQ_AMIGA_CIAA_FLG  (IRQ_MACHSPEC | 18)
+#define IRQ_AMIGA_CIAB      (IRQ_MACHSPEC | 19)
+#define IRQ_AMIGA_CIAB_TA   (IRQ_MACHSPEC | 19)
+#define IRQ_AMIGA_CIAB_TB   (IRQ_MACHSPEC | 20)
+#define IRQ_AMIGA_CIAB_ALRM (IRQ_MACHSPEC | 21)
+#define IRQ_AMIGA_CIAB_SP   (IRQ_MACHSPEC | 22)
+#define IRQ_AMIGA_CIAB_FLG  (IRQ_MACHSPEC | 23)
 
 #define IRQ_FLOPPY	    IRQ_AMIGA_DSKBLK
 
@@ -88,12 +94,30 @@
 #define IF_DSKBLK   0x0002	/* diskblock DMA finished */
 #define IF_TBE	    0x0001	/* serial transmit buffer empty interrupt */
 
+struct irq_server {
+	unsigned short count, reentrance;
+};
+
+extern void amiga_do_irq(int irq, struct pt_regs *fp);
+extern void amiga_do_irq_list(int irq, struct pt_regs *fp, struct irq_server *server);
+
 /* CIA interrupt control register bits */
 
-#define CIA_ICR_TA   0x01
-#define CIA_ICR_TB   0x02
-#define CIA_ICR_ALRM 0x04
-#define CIA_ICR_SP   0x08
-#define CIA_ICR_FLG  0x10
+#define CIA_ICR_TA	0x01
+#define CIA_ICR_TB	0x02
+#define CIA_ICR_ALRM	0x04
+#define CIA_ICR_SP	0x08
+#define CIA_ICR_FLG	0x10
+#define CIA_ICR_ALL	0x1f
+#define CIA_ICR_SETCLR	0x80
+
+/* to access the interrupt control registers of CIA's use only
+** these functions, they behave exactly like the amiga os routines
+*/
+
+extern struct ciabase ciaa_base, ciab_base;
+
+extern unsigned char cia_set_irq(struct ciabase *base, unsigned char mask);
+extern unsigned char cia_able_irq(struct ciabase *base, unsigned char mask);
 
 #endif /* asm-m68k/amigaints.h */
diff -urN pc-2.0.21/include/asm-m68k/atari_SLM.h linux-2.0.21/include/asm-m68k/atari_SLM.h
--- pc-2.0.21/include/asm-m68k/atari_SLM.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/include/asm-m68k/atari_SLM.h	Fri Sep 27 01:06:08 1996
@@ -0,0 +1,28 @@
+
+#ifndef _ATARI_SLM_H
+#define _ATARI_SLM_H
+
+/* Atari SLM laser printer specific ioctls */
+
+#define	SLMIOGSTAT	0xa100
+#define	SLMIOGPSIZE	0xa101
+#define	SLMIOGMFEED	0xa102
+
+#define	SLMIORESET	0xa140
+
+#define	SLMIOSPSIZE	0xa181
+#define	SLMIOSMFEED	0xa182
+
+/* Status returning structure (SLMIOGSTAT) */
+struct SLM_status {
+	int		stat;		/* numeric status code */
+	char	str[40];	/* status string */
+};
+
+/* Paper size structure (SLMIO[GS]PSIZE) */
+struct SLM_paper_size {
+	int		width;
+	int		height;
+};
+
+#endif /* _ATARI_SLM_H */
diff -urN pc-2.0.21/include/asm-m68k/atari_acsi.h linux-2.0.21/include/asm-m68k/atari_acsi.h
--- pc-2.0.21/include/asm-m68k/atari_acsi.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/include/asm-m68k/atari_acsi.h	Sat Aug 24 14:45:06 1996
@@ -0,0 +1,37 @@
+#ifndef _ASM_ATARI_ACSI_H
+#define _ASM_ATARI_ACSI_H
+
+/* Functions exported by drivers/block/acsi.c */
+
+void acsi_delay_start( void );
+void acsi_delay_end( long usec );
+int acsi_wait_for_IRQ( unsigned timeout );
+int acsi_wait_for_noIRQ( unsigned timeout );
+int acsicmd_nodma( const char *cmd, int enable);
+int acsi_getstatus( void );
+int acsi_extstatus( char *buffer, int cnt );
+void acsi_end_extstatus( void );
+int acsi_extcmd( unsigned char *buffer, int cnt );
+
+/* The ACSI buffer is guarantueed to reside in ST-RAM and may be used by other
+ * drivers that work on the ACSI bus, too. It's data are valid only as long as
+ * the ST-DMA is locked. */
+extern char *acsi_buffer;
+extern unsigned long phys_acsi_buffer;
+
+/* Utility macros */
+
+/* Send one data byte over the bus and set mode for next operation
+ * with one move.l -- Atari recommends this...
+ */
+
+#define DMA_LONG_WRITE(data,mode)							\
+    do {													\
+		*((unsigned long *)&dma_wd.fdc_acces_seccount) = 	\
+			((data)<<16) | (mode);							\
+	} while(0)
+
+#define ENABLE_IRQ()	atari_turnon_irq( IRQ_MFP_ACSI )
+#define DISABLE_IRQ()	atari_turnoff_irq( IRQ_MFP_ACSI )
+
+#endif /* _ASM_ATARI_ACSI_H */
diff -urN pc-2.0.21/include/asm-m68k/atari_rootsec.h linux-2.0.21/include/asm-m68k/atari_rootsec.h
--- pc-2.0.21/include/asm-m68k/atari_rootsec.h	Wed Dec 27 21:46:46 1995
+++ linux-2.0.21/include/asm-m68k/atari_rootsec.h	Wed Sep 18 19:03:36 1996
@@ -29,6 +29,6 @@
   u_long bsl_st;		/* start of bad sector list */
   u_long bsl_cnt;		/* length of bad sector list */
   u_short checksum;		/* checksum for bootable disks */
-};
+} __attribute__((__packed__));
 
 #endif /* _LINUX_ATARI_ROOTSEC_H */
diff -urN pc-2.0.21/include/asm-m68k/atari_stdma.h linux-2.0.21/include/asm-m68k/atari_stdma.h
--- pc-2.0.21/include/asm-m68k/atari_stdma.h	Fri Apr 19 01:35:24 1996
+++ linux-2.0.21/include/asm-m68k/atari_stdma.h	Thu Sep 26 22:37:02 1996
@@ -8,7 +8,7 @@
 
 /***************************** Prototypes *****************************/
 
-void stdma_lock(isrfunc isr, void *data); 
+void stdma_lock(void (*handler)(int, void *, struct pt_regs *), void *data); 
 void stdma_release( void );
 int stdma_others_waiting( void );
 int stdma_islocked( void );
diff -urN pc-2.0.21/include/asm-m68k/atarihw.h linux-2.0.21/include/asm-m68k/atarihw.h
--- pc-2.0.21/include/asm-m68k/atarihw.h	Mon May 20 06:54:28 1996
+++ linux-2.0.21/include/asm-m68k/atarihw.h	Thu Sep 26 22:37:02 1996
@@ -7,6 +7,10 @@
 ** 5/1/94 Roman Hodek:
 **   Added definitions for TT specific chips.
 **
+** 1996-09-13 lars brinkhoff <f93labr@dd.chalmers.se>:
+**   Finally added definitions for the matrix/codec and the DSP56001 host
+**   interface.
+**
 ** 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.
@@ -48,7 +52,7 @@
 
 {
 	if (writeflag) {
-		if (!is_medusa || m68k_is040or060 == 6)
+		if (!is_medusa || CPU_IS_060)
 			cache_push( paddr, len );
 	}
 	else {
@@ -273,9 +277,47 @@
 
 /* 
 ** Falcon DMA Sound Subsystem
-** not implemented yet
  */     
 
+#define MATRIX_BASE (0xffff8930)
+struct MATRIX
+{
+  u_short source;
+  u_short destination;
+  u_char external_frequency_divider;
+  u_char internal_frequency_divider;
+};
+#define matrix (*(volatile struct MATRIX *)MATRIX_BASE)
+
+#define CODEC_BASE (0xffff8936)
+struct CODEC
+{
+  u_char tracks;
+  u_char input_source;
+#define CODEC_SOURCE_MATRIX     1
+#define CODEC_SOURCE_ADC        2
+  u_char adc_source;
+#define ADC_SOURCE_RIGHT_PSG    1
+#define ADC_SOURCE_LEFT_PSG     2
+  u_char gain;
+#define CODEC_GAIN_RIGHT        0x0f
+#define CODEC_GAIN_LEFT         0xf0
+  u_char attenuation;
+#define CODEC_ATTENUATION_RIGHT 0x0f
+#define CODEC_ATTENUATION_LEFT  0xf0
+  u_char unused1;
+  u_char status;
+#define CODEC_OVERFLOW_RIGHT    1
+#define CODEC_OVERFLOW_LEFT     2
+  u_char unused2, unused3, unused4, unused5;
+  u_char gpio_directions;
+#define GPIO_IN                 0
+#define GPIO_OUT                1
+  u_char unused6;
+  u_char gpio_data;
+};
+#define codec (*(volatile struct CODEC *)CODEC_BASE)
+
 /*
 ** Falcon Blitter
 */
@@ -321,13 +363,13 @@
  };
 # define scc ((*(volatile struct SCC*)SCC_BAS))
 
-/* The ESCC (Z85230) in an Atari ST. The channels are revered! */
+/* The ESCC (Z85230) in an Atari ST. The channels are reversed! */
 # define st_escc ((*(volatile struct SCC*)0xfffffa31))
 # define st_escc_dsr ((*(volatile char *)0xfffffa39))
 
 /* TT SCC DMA Controller (same chip as SCSI DMA) */
 
-#define	TT_SCC_DMA_BAS	(0xffff8c01)
+#define	TT_SCC_DMA_BAS	(0xffff8c00)
 #define	tt_scc_dma	((*(volatile struct TT_DMA *)TT_SCC_DMA_BAS))
 
 /*
@@ -344,8 +386,41 @@
 
 /*
 ** Falcon DSP Host Interface
-** not implemented yet
  */
+
+#define DSP56K_HOST_INTERFACE_BASE (0xffffa200)
+struct DSP56K_HOST_INTERFACE {
+  u_char icr;
+#define DSP56K_ICR_RREQ	0x01
+#define DSP56K_ICR_TREQ	0x02
+#define DSP56K_ICR_HF0	0x08
+#define DSP56K_ICR_HF1	0x10
+#define DSP56K_ICR_HM0	0x20
+#define DSP56K_ICR_HM1	0x40
+#define DSP56K_ICR_INIT	0x80
+  
+  u_char cvr;
+#define DSP56K_CVR_HV_MASK 0x1f
+#define DSP56K_CVR_HC	0x80
+
+  u_char isr;
+#define DSP56K_ISR_RXDF	0x01
+#define DSP56K_ISR_TXDE	0x02
+#define DSP56K_ISR_TRDY	0x04
+#define DSP56K_ISR_HF2	0x08
+#define DSP56K_ISR_HF3	0x10
+#define DSP56K_ISR_DMA	0x40
+#define DSP56K_ISR_HREQ	0x80
+  
+  u_char ivr;
+
+  union {
+    u_char b[4];
+    u_short w[2];
+    u_long l;
+  } data;
+};
+#define dsp56k_host_interface ((*(volatile struct DSP56K_HOST_INTERFACE *)DSP56K_HOST_INTERFACE_BASE))
  
 /*
 ** MFP 68901
diff -urN pc-2.0.21/include/asm-m68k/atariints.h linux-2.0.21/include/asm-m68k/atariints.h
--- pc-2.0.21/include/asm-m68k/atariints.h	Mon May 20 06:54:28 1996
+++ linux-2.0.21/include/asm-m68k/atariints.h	Thu Sep 26 22:37:02 1996
@@ -216,5 +216,6 @@
 }
 
 unsigned long atari_register_vme_int( void );
+void atari_unregister_vme_int( unsigned long );
 
 #endif /* linux/atariints.h */
diff -urN pc-2.0.21/include/asm-m68k/bitops.h linux-2.0.21/include/asm-m68k/bitops.h
--- pc-2.0.21/include/asm-m68k/bitops.h	Mon May 20 06:54:29 1996
+++ linux-2.0.21/include/asm-m68k/bitops.h	Mon Sep 16 23:54:11 1996
@@ -59,11 +59,11 @@
 	if (!size)
 		return 0;
 
+	size = (size >> 5) + ((size & 31) > 0);
 	while (*p++ == allones)
 	{
-		if (size <= 32)
+		if (--size == 0)
 			return (p - addr) << 5;
-		size -= 32;
 	}
 
 	num = ~*--p;
@@ -123,11 +123,11 @@
 	if (!size)
 		return 0;
 
+	size = (size >> 4) + ((size & 15) > 0);
 	while (*p++ == 0xffff)
 	{
-		if (size <= 16)
+		if (--size == 0)
 			return (p - addr) << 4;
-		size -= 16;
 	}
 
 	num = ~*--p;
@@ -203,11 +203,11 @@
 	if (!size)
 		return 0;
 
+	size = (size >> 5) + ((size & 31) > 0);
 	while (*p++ == ~0UL)
 	{
-		if (size <= 32)
+		if (--size == 0)
 			return (p - addr) << 5;
-		size -= 32;
 	}
 
 	--p;
@@ -237,6 +237,21 @@
 	/* No zero yet, search remaining full bytes for a zero */
 	res = ext2_find_first_zero_bit (p, size - 32 * (p - addr));
 	return (p - addr) * 32 + res;
+}
+
+/* Byte swapping. */
+
+extern __inline__ unsigned short
+swab16 (unsigned short val)
+{
+	return (val << 8) | (val >> 8);
+}
+
+extern __inline__ unsigned int
+swab32 (unsigned int val)
+{
+	__asm__ ("rolw #8,%0; swap %0; rolw #8,%0" : "=d" (val) : "0" (val));
+	return val;
 }
 
 #endif /* _M68K_BITOPS_H */
diff -urN pc-2.0.21/include/asm-m68k/bootinfo.h linux-2.0.21/include/asm-m68k/bootinfo.h
--- pc-2.0.21/include/asm-m68k/bootinfo.h	Mon May 20 06:54:29 1996
+++ linux-2.0.21/include/asm-m68k/bootinfo.h	Thu Jan  1 01:00:00 1970
@@ -1,289 +0,0 @@
-/*
-** asm/bootinfo.h -- Definition of the Linux/68K boot information structure
-**
-** Copyright 1992 by Greg Harp
-**
-** 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.
-**
-** Created 09/29/92 by Greg Harp
-**
-** 5/2/94 Roman Hodek:
-**   Added bi_atari part of the machine dependent union bi_un; for now it
-**	 contains just a model field to distinguish between TT and Falcon.
-*/
-
-#ifndef BOOTINFO_H
-#define BOOTINFO_H
-
-#include <asm/zorro.h>
-
-/*
- * Amiga specific part of bootinfo structure.
- */
-
-#define NUM_AUTO    16
-
-#ifndef __ASSEMBLY__
-
-#define AMIGAHW_DECLARE(name)	unsigned name : 1
-#define AMIGAHW_SET(name)	(boot_info.bi_amiga.hw_present.name = 1)
-#define AMIGAHW_PRESENT(name)	(boot_info.bi_amiga.hw_present.name)
-
-struct bi_Amiga {
-  int model;				/* Amiga Model (3000?) */
-  int num_autocon;			/* # of autoconfig devices found */
-  struct ConfigDev autocon[NUM_AUTO];	/* up to 16 autoconfig devices */
-#ifdef HACKER_KERNEL
-  void (*exit_func)(void);		/* addr of function to exit kernel */
-  unsigned long chip_addr;		/* start of chip memory (bytes) */
-#endif
-  unsigned long chip_size;		/* size of chip memory (bytes) */
-  unsigned char vblank; 		/* VBLANK frequency */
-  unsigned char psfreq; 		/* power supply frequency */
-  unsigned long eclock; 		/* EClock frequency */
-  unsigned long chipset;		/* native chipset present */
-  struct {
-    /* video hardware */
-    AMIGAHW_DECLARE(AMI_VIDEO);		/* Amiga Video */
-    AMIGAHW_DECLARE(AMI_BLITTER);	/* Amiga Blitter */
-    AMIGAHW_DECLARE(AMBER_FF);		/* Amber Flicker Fixer */
-    /* sound hardware */
-    AMIGAHW_DECLARE(AMI_AUDIO);		/* Amiga Audio */
-    /* disk storage interfaces */
-    AMIGAHW_DECLARE(AMI_FLOPPY);	/* Amiga Floppy */
-    AMIGAHW_DECLARE(A3000_SCSI);	/* SCSI (wd33c93, A3000 alike) */
-    AMIGAHW_DECLARE(A4000_SCSI);	/* SCSI (ncr53c710, A4000T alike) */
-    AMIGAHW_DECLARE(A1200_IDE);		/* IDE (A1200 alike) */
-    AMIGAHW_DECLARE(A4000_IDE);		/* IDE (A4000 alike) */
-    AMIGAHW_DECLARE(CD_ROM);		/* CD ROM drive */
-    /* other I/O hardware */
-    AMIGAHW_DECLARE(AMI_KEYBOARD);	/* Amiga Keyboard */
-    AMIGAHW_DECLARE(AMI_MOUSE);		/* Amiga Mouse */
-    AMIGAHW_DECLARE(AMI_SERIAL);	/* Amiga Serial */
-    AMIGAHW_DECLARE(AMI_PARALLEL);	/* Amiga Parallel */
-    /* real time clocks */
-    AMIGAHW_DECLARE(A2000_CLK);		/* Hardware Clock (A2000 alike) */
-    AMIGAHW_DECLARE(A3000_CLK);		/* Hardware Clock (A3000 alike) */
-    /* supporting hardware */
-    AMIGAHW_DECLARE(CHIP_RAM);		/* Chip RAM */
-    AMIGAHW_DECLARE(PAULA);		/* Paula (8364) */
-    AMIGAHW_DECLARE(DENISE);		/* Denise (8362) */
-    AMIGAHW_DECLARE(DENISE_HR);		/* Denise (8373) */
-    AMIGAHW_DECLARE(LISA);		/* Lisa (8375) */
-    AMIGAHW_DECLARE(AGNUS_PAL);		/* Normal/Fat PAL Agnus (8367/8371) */
-    AMIGAHW_DECLARE(AGNUS_NTSC);	/* Normal/Fat NTSC Agnus (8361/8370) */
-    AMIGAHW_DECLARE(AGNUS_HR_PAL);	/* Fat Hires PAL Agnus (8372) */
-    AMIGAHW_DECLARE(AGNUS_HR_NTSC);	/* Fat Hires NTSC Agnus (8372) */
-    AMIGAHW_DECLARE(ALICE_PAL);		/* PAL Alice (8374) */
-    AMIGAHW_DECLARE(ALICE_NTSC);	/* NTSC Alice (8374) */
-    AMIGAHW_DECLARE(MAGIC_REKICK);	/* A3000 Magic Hard Rekick */
-    AMIGAHW_DECLARE(ZORRO);		/* Zorro AutoConfig */
-  } hw_present;
-};
-
-#else	/* __ASSEMBLY__ */
- 
-BI_amiga_model		= BI_un
-BI_amiga_num_autcon	= BI_amiga_model+4
-BI_amiga_autocon	= BI_amiga_num_autcon+4
-#ifdef HACKER_KERNEL
-BI_amiga_exit_func	= BI_amiga_autocon+(CD_sizeof*NUM_AUTO)
-BI_amiga_chip_addr	= BI_amiga_exit_func+4
-BI_amiga_chip_size	= BI_amiga_chip_addr+4
-#else
-BI_amiga_chip_size	= BI_amiga_autocon+(CD_sizeof*NUM_AUTO)
-#endif
-BI_amiga_vblank		= BI_amiga_chip_size+4
-BI_amiga_psfreq		= BI_amiga_vblank+1
-BI_amiga_eclock		= BI_amiga_psfreq+1
-BI_amiga_chipset	= BI_amiga_eclock+4
-BI_amiga_hw_present	= BI_amiga_chipset+4
-
-#endif	/* __ASSEMBLY__ */
-
-/* Atari specific part of bootinfo */
-
-/*
- * Define several Hardware-Chips for indication so that for the ATARI we do
- * no longer decide whether it is a Falcon or other machine . It's just
- * important what hardware the machine uses
- */
-
-/* ++roman 08/08/95: rewritten from ORing constants to a C bitfield */
-
-#ifndef __ASSEMBLY__
-
-#define ATARIHW_DECLARE(name)	unsigned name : 1
-#define ATARIHW_SET(name)	(boot_info.bi_atari.hw_present.name = 1)
-#define ATARIHW_PRESENT(name)	(boot_info.bi_atari.hw_present.name)
-
-struct bi_Atari {
-  struct {
-    /* video hardware */
-    ATARIHW_DECLARE(STND_SHIFTER);	/* ST-Shifter - no base low ! */
-    ATARIHW_DECLARE(EXTD_SHIFTER);	/* STe-Shifter - 24 bit address */
-    ATARIHW_DECLARE(TT_SHIFTER);	/* TT-Shifter */
-    ATARIHW_DECLARE(VIDEL_SHIFTER);	/* Falcon-Shifter */
-    /* sound hardware */
-    ATARIHW_DECLARE(YM_2149);		/* Yamaha YM 2149 */
-    ATARIHW_DECLARE(PCM_8BIT);		/* PCM-Sound in STe-ATARI */
-    ATARIHW_DECLARE(CODEC);		/* CODEC Sound (Falcon) */
-    /* disk storage interfaces */
-    ATARIHW_DECLARE(TT_SCSI);		/* Directly mapped NCR5380 */
-    ATARIHW_DECLARE(ST_SCSI);		/* NCR5380 via ST-DMA (Falcon) */
-    ATARIHW_DECLARE(ACSI);		/* Standard ACSI like in STs */
-    ATARIHW_DECLARE(IDE);		/* IDE Interface */
-    ATARIHW_DECLARE(FDCSPEED);		/* 8/16 MHz switch for FDC */
-    /* other I/O hardware */
-    ATARIHW_DECLARE(ST_MFP);		/* The ST-MFP (there should
-					   be no Atari without
-					   it... but who knows?) */
-    ATARIHW_DECLARE(TT_MFP);		/* 2nd MFP */
-    ATARIHW_DECLARE(SCC);		/* Serial Communications Contr. */
-    ATARIHW_DECLARE(ST_ESCC);		/* SCC Z83230 in an ST */
-    ATARIHW_DECLARE(ANALOG_JOY);	/* Paddle Interface for STe
-					   and Falcon */
-    ATARIHW_DECLARE(MICROWIRE);		/* Microwire Interface */
-    /* DMA */
-    ATARIHW_DECLARE(STND_DMA);		/* 24 Bit limited ST-DMA */
-    ATARIHW_DECLARE(EXTD_DMA);		/* 32 Bit ST-DMA */
-    ATARIHW_DECLARE(SCSI_DMA);		/* DMA for the NCR5380 */
-    ATARIHW_DECLARE(SCC_DMA);		/* DMA for the SCC */
-    /* real time clocks */
-    ATARIHW_DECLARE(TT_CLK);		/* TT compatible clock chip */
-    ATARIHW_DECLARE(MSTE_CLK);		/* Mega ST(E) clock chip */
-    /* supporting hardware */
-    ATARIHW_DECLARE(SCU);		/* System Control Unit */
-    ATARIHW_DECLARE(BLITTER);		/* Blitter */
-    ATARIHW_DECLARE(VME);		/* VME Bus */
-  } hw_present;
-  unsigned long mch_cookie;		/* _MCH cookie from TOS */
-};
-
-/* mch_cookie values (upper word) */
-#define	ATARI_MCH_ST		0
-#define	ATARI_MCH_STE		1
-#define	ATARI_MCH_TT		2
-#define	ATARI_MCH_FALCON	3
-
-struct mem_info {
-  unsigned long addr;		/* physical address of memory chunk */
-  unsigned long size;		/* length of memory chunk (in bytes) */
-};
-
-#else	/* __ASSEMBLY__ */
-
-MI_addr		= 0
-MI_size		= MI_addr+4
-MI_sizeof	= MI_size+4
-
-#endif /* __ASSEMBLY__ */
-
-#define NUM_MEMINFO  4
-
-#define MACH_AMIGA   1
-#define MACH_ATARI   2
-#define MACH_MAC     3
-
-/*
- * CPU and FPU types
- */
-
-#define CPUB_68020 0
-#define CPUB_68030 1
-#define CPUB_68040 2
-#define CPUB_68060 3
-#define FPUB_68881 5
-#define FPUB_68882 6
-#define FPUB_68040 7	/* Internal FPU */
-#define FPUB_68060 8	/* Internal FPU */
-
-#define CPU_68020    (1<<CPUB_68020)
-#define CPU_68030    (1<<CPUB_68030)
-#define CPU_68040    (1<<CPUB_68040)
-#define CPU_68060    (1<<CPUB_68060)
-#define CPU_MASK     (31)
-#define FPU_68881    (1<<FPUB_68881)
-#define FPU_68882    (1<<FPUB_68882)
-#define FPU_68040    (1<<FPUB_68040)	/* Internal FPU */
-#define FPU_68060    (1<<FPUB_68060)	/* Internal FPU */
-
-#define CL_SIZE      (256)
-
-#ifndef __ASSEMBLY__
-
-#define MACH_IS_AMIGA	(boot_info.machtype == MACH_AMIGA)
-#define MACH_IS_ATARI	(boot_info.machtype == MACH_ATARI)
-
-struct bootinfo {
-  unsigned long machtype;		/* machine type */
-  unsigned long cputype;		/* system CPU & FPU */
-  struct mem_info memory[NUM_MEMINFO];	/* memory description */
-  int num_memory;			/* # of memory blocks found */
-  unsigned long ramdisk_size;		/* ramdisk size in 1024 byte blocks */
-  unsigned long ramdisk_addr;		/* address of the ram disk in mem */
-  char command_line[CL_SIZE];		/* kernel command line parameters */
-  union {
-    struct bi_Amiga bi_ami; 	/* Amiga specific information */
-    struct bi_Atari bi_ata; 	/* Atari specific information */
-  } bi_un;
-};
-#define bi_amiga bi_un.bi_ami
-#define bi_atari bi_un.bi_ata
-#define bi_mac	 bi_un.bi_mac
-
-extern struct bootinfo
-    boot_info;
-
-#else	/* __ASSEMBLY__ */
-
-BI_machtype	= 0
-BI_cputype	= BI_machtype+4
-BI_memory	= BI_cputype+4
-BI_num_memory	= BI_memory+(MI_sizeof*NUM_MEMINFO)
-BI_ramdisk_size	= BI_num_memory+4
-BI_ramdisk_addr	= BI_ramdisk_size+4
-BI_command_line	= BI_ramdisk_addr+4
-BI_un		= BI_command_line+CL_SIZE
-
-#endif /* __ASSEMBLY__ */
-
-
-/*
- * Stuff for bootinfo interface versioning
- *
- * At the start of kernel code, a 'struct bootversion' is located. bootstrap
- * checks for a matching version of the interface before booting a kernel, to
- * avoid user confusion if kernel and bootstrap don't work together :-)
- *
- * If incompatible changes are made to the bootinfo interface, the major
- * number below should be stepped (and the minor reset to 0) for the
- * appropriate machine. If a change is backward-compatible, the minor should
- * be stepped. "Backwards-compatible" means that booting will work, but
- * certain features may not.
- */
-
-#define BOOTINFOV_MAGIC			0x4249561A	/* 'BIV^Z' */
-#define MK_BI_VERSION(major,minor)	(((major)<<16)+(minor))
-#define BI_VERSION_MAJOR(v)		(((v) >> 16) & 0xffff)
-#define BI_VERSION_MINOR(v)		((v) & 0xffff)
-
-#ifndef __ASSEMBLY__
-
-struct bootversion {
-    unsigned short branch;
-    unsigned long magic;
-    struct {
-       unsigned long machtype;
-       unsigned long version;
-    } machversions[0];
-};
-
-#endif /* __ASSEMBLY__ */
-
-#define AMIGA_BOOTI_VERSION    MK_BI_VERSION( 1, 0 )
-#define ATARI_BOOTI_VERSION    MK_BI_VERSION( 1, 0 )
-
-#endif /* BOOTINFO_H */
diff -urN pc-2.0.21/include/asm-m68k/byteorder.h linux-2.0.21/include/asm-m68k/byteorder.h
--- pc-2.0.21/include/asm-m68k/byteorder.h	Sun Mar 31 00:09:52 1996
+++ linux-2.0.21/include/asm-m68k/byteorder.h	Sat Aug 24 14:45:12 1996
@@ -1,14 +1,47 @@
 #ifndef _M68K_BYTEORDER_H
 #define _M68K_BYTEORDER_H
 
-#ifdef __KERNEL__
-#define __BIG_ENDIAN
+#ifndef __BIG_ENDIAN
+#define __BIG_ENDIAN 4321
 #endif
+
+#ifndef __BIG_ENDIAN_BITFIELD
 #define __BIG_ENDIAN_BITFIELD
+#endif
+
+#undef ntohl
+#undef ntohs
+#undef htonl
+#undef htons
+
+extern unsigned long int	ntohl(unsigned long int);
+extern unsigned short int	ntohs(unsigned short int);
+extern unsigned long int	htonl(unsigned long int);
+extern unsigned short int	htons(unsigned short int);
+
+extern __inline__ unsigned long int	__ntohl(unsigned long int);
+extern __inline__ unsigned short int	__ntohs(unsigned short int);
 
-#define ntohl(x) x
-#define ntohs(x) x
-#define htonl(x) x
-#define htons(x) x
+extern __inline__ unsigned long int
+__ntohl(unsigned long int x)
+{
+	return x;
+}
+
+extern __inline__ unsigned short int
+__ntohs(unsigned short int x)
+{
+	return x;
+}
+
+#define __htonl(x) __ntohl(x)
+#define __htons(x) __ntohs(x)
+
+#ifdef __OPTIMIZE__
+#define ntohl(x) __ntohl(x)
+#define ntohs(x) __ntohs(x)
+#define htonl(x) __htonl(x)
+#define htons(x) __htons(x)
+#endif
 
 #endif
diff -urN pc-2.0.21/include/asm-m68k/delay.h linux-2.0.21/include/asm-m68k/delay.h
--- pc-2.0.21/include/asm-m68k/delay.h	Thu May 16 08:05:11 1996
+++ linux-2.0.21/include/asm-m68k/delay.h	Mon Sep 16 23:54:12 1996
@@ -7,13 +7,10 @@
  * Delay routines, using a pre-computed "loops_per_second" value.
  */
 
-extern __inline__ void __delay(int loops)
+extern __inline__ void __delay(unsigned long loops)
 {
-	__asm__ __volatile__ ("\n\tmovel %0,%/d0\n1:\tsubql #1,%/d0\n\t"
-			      "bpls 1b\n"
-		: /* no outputs */
-		: "g" (loops)
-		: "d0");
+	__asm__ __volatile__ ("1: subql #1,%0; jcc 1b"
+		: "=d" (loops) : "0" (loops));
 }
 
 /*
@@ -25,23 +22,22 @@
  */
 extern __inline__ void udelay(unsigned long usecs)
 {
-	usecs *= 0x000010c6;		/* 2**32 / 1000000 */
+	unsigned long tmp;
 
-	__asm__ __volatile__ ("mulul %1,%0:%2"
-	     : "=d" (usecs)
-	     : "d" (usecs),
-	       "d" (loops_per_sec));
+	usecs *= 4295;		/* 2**32 / 1000000 */
+	__asm__ ("mulul %2,%0:%1"
+		: "=d" (usecs), "=d" (tmp)
+		: "d" (usecs), "1" (loops_per_sec));
 	__delay(usecs);
 }
 
 extern __inline__ unsigned long muldiv(unsigned long a, unsigned long b, unsigned long c)
 {
-	__asm__ ("mulul %1,%/d0:%0\n\tdivul %2,%/d0:%0"
-		 :"=d" (a)
-		 :"d" (b),
-		 "d" (c),
-		 "0" (a)
-		 :"d0");
+	unsigned long tmp;
+
+	__asm__ ("mulul %2,%0:%1; divul %3,%0:%1"
+		: "=d" (tmp), "=d" (a)
+		: "d" (b), "d" (c), "1" (a));
 	return a;
 }
 
diff -urN pc-2.0.21/include/asm-m68k/errno.h linux-2.0.21/include/asm-m68k/errno.h
--- pc-2.0.21/include/asm-m68k/errno.h	Wed Dec 27 21:46:55 1995
+++ linux-2.0.21/include/asm-m68k/errno.h	Sat Aug 24 14:45:15 1996
@@ -58,7 +58,9 @@
 #define	ENOANO		55	/* No anode */
 #define	EBADRQC		56	/* Invalid request code */
 #define	EBADSLT		57	/* Invalid slot */
-#define	EDEADLOCK	58	/* File locking deadlock error */
+
+#define	EDEADLOCK	EDEADLK
+
 #define	EBFONT		59	/* Bad font file format */
 #define	ENOSTR		60	/* Device not a stream */
 #define	ENODATA		61	/* No data available */
diff -urN pc-2.0.21/include/asm-m68k/ide.h linux-2.0.21/include/asm-m68k/ide.h
--- pc-2.0.21/include/asm-m68k/ide.h	Tue Jul 23 10:33:32 1996
+++ linux-2.0.21/include/asm-m68k/ide.h	Thu Sep 26 22:37:02 1996
@@ -19,42 +19,15 @@
 #include <asm/atari_stdma.h>
 #endif /* CONFIG_ATARI */
 
-#include <asm/bootinfo.h>
+#include <asm/setup.h>
 
-struct hd_regs_struct {
-  unsigned int hd_error,
-  hd_nsector,
-  hd_sector,
-  hd_lcyl,
-  hd_hcyl,
-  hd_select,
-  hd_status;
-};
-
-static struct hd_regs_struct hd_regs;
-static void probe_m68k_ide (void);
-
-/* Undefine these again, they were defined for the PC. */
-#undef IDE_ERROR_OFFSET
-#undef IDE_NSECTOR_OFFSET
-#undef IDE_SECTOR_OFFSET
-#undef IDE_LCYL_OFFSET
-#undef IDE_HCYL_OFFSET
-#undef IDE_SELECT_OFFSET
-#undef IDE_STATUS_OFFSET
-#undef IDE_FEATURE_OFFSET
-#undef IDE_COMMAND_OFFSET
-#undef SELECT_DRIVE
-
-#define IDE_ERROR_OFFSET	hd_regs.hd_error
-#define IDE_NSECTOR_OFFSET	hd_regs.hd_nsector
-#define IDE_SECTOR_OFFSET	hd_regs.hd_sector
-#define IDE_LCYL_OFFSET		hd_regs.hd_lcyl
-#define IDE_HCYL_OFFSET		hd_regs.hd_hcyl
-#define IDE_SELECT_OFFSET	hd_regs.hd_select
-#define IDE_STATUS_OFFSET	hd_regs.hd_status
-#define IDE_FEATURE_OFFSET	IDE_ERROR_OFFSET
-#define IDE_COMMAND_OFFSET	IDE_STATUS_OFFSET
+typedef unsigned char * ide_ioreg_t;
+
+#define IDE_MEM_MAPPED_IO
+
+#ifndef MAX_HWIFS
+#define MAX_HWIFS	1
+#endif
 
 #undef SUPPORT_VLB_SYNC
 #define SUPPORT_VLB_SYNC 0
@@ -86,8 +59,6 @@
 #else /* !defined(CONFIG_ATARI) */
 #define	STI()	sti()
 #endif
-
-#define SELECT_DRIVE(hwif,drive)  OUT_BYTE((drive)->select.all, hwif->io_base+IDE_SELECT_OFFSET);
 
 #define insl(data_reg, buffer, wcount) insw(data_reg, buffer, wcount<<1)
 #define outsl(data_reg, buffer, wcount) outsw(data_reg, buffer, wcount<<1)
diff -urN pc-2.0.21/include/asm-m68k/ioctl.h linux-2.0.21/include/asm-m68k/ioctl.h
--- pc-2.0.21/include/asm-m68k/ioctl.h	Sat Mar 23 13:31:14 1996
+++ linux-2.0.21/include/asm-m68k/ioctl.h	Sat Aug 24 14:45:17 1996
@@ -1,4 +1,4 @@
-/* $Id: ioctl.h,v 1.4 1996/03/23 12:31:12 root Exp root $
+/* $Id: ioctl.h,v 1.1 1996/08/24 12:43:44 root Exp $
  *
  * linux/ioctl.h for Linux by H.H. Bergman.
  */
diff -urN pc-2.0.21/include/asm-m68k/irq.h linux-2.0.21/include/asm-m68k/irq.h
--- pc-2.0.21/include/asm-m68k/irq.h	Mon May 20 06:54:29 1996
+++ linux-2.0.21/include/asm-m68k/irq.h	Thu Sep 26 22:26:10 1996
@@ -7,15 +7,21 @@
 #include <linux/config.h>
 
 /*
+ * # of m68k interrupts
+ */
+
+#define SYS_IRQS 8
+
+/*
  * This should be the same as the max(NUM_X_SOURCES) for all the
  * different m68k hosts compiled into the kernel.
  * 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)
-#define NR_IRQS 72
+#define NR_IRQS (72+SYS_IRQS)
 #else
-#define NR_IRQS 24
+#define NR_IRQS (24+SYS_IRQS)
 #endif
 
 /*
@@ -28,19 +34,19 @@
  * that routine requires service.
  */
 
-#define IRQ1		 (1)    /* level 1 interrupt */
-#define IRQ2		 (2)    /* level 2 interrupt */
-#define IRQ3		 (3)    /* level 3 interrupt */
-#define IRQ4		 (4)    /* level 4 interrupt */
-#define IRQ5		 (5)    /* level 5 interrupt */
-#define IRQ6		 (6)    /* level 6 interrupt */
-#define IRQ7		 (7)    /* level 7 interrupt (non-maskable) */
+#define IRQ1		(1)	/* level 1 interrupt */
+#define IRQ2		(2)	/* level 2 interrupt */
+#define IRQ3		(3)	/* level 3 interrupt */
+#define IRQ4		(4)	/* level 4 interrupt */
+#define IRQ5		(5)	/* level 5 interrupt */
+#define IRQ6		(6)	/* level 6 interrupt */
+#define IRQ7		(7)	/* level 7 interrupt (non-maskable) */
 
 /*
  * "Generic" interrupt sources
  */
 
-#define IRQ_SCHED_TIMER  (8)    /* interrupt source for scheduling timer */
+#define IRQ_SCHED_TIMER	(8)    /* interrupt source for scheduling timer */
 
 /*
  * Machine specific interrupt sources.
@@ -50,67 +56,46 @@
  * The machine specific files define these sources.
  */
 
-#define IRQ_MACHSPEC	 (0x10000000L)
+#define IRQ_MACHSPEC	(0x10000000L)
+#define IRQ_IDX(irq)	((irq) & ~IRQ_MACHSPEC)
 
-#ifndef ISRFUNC_T
-struct pt_regs;
-typedef void (*isrfunc) (int irq, struct pt_regs * regs, void *data);
-#define ISRFUNC_T
-#endif /* ISRFUNC_T */
+/*
+ * various flags for request_irq()
+ */
+#define IRQ_FLG_LOCK	(0x0001)	/* handler is not replaceable	*/
+#define IRQ_FLG_REPLACE	(0x0002)	/* replace existing handler	*/
+#define IRQ_FLG_FAST	(0x0004)
+#define IRQ_FLG_SLOW	(0x0008)
+#define IRQ_FLG_STD	(0x8000)	/* internally used		*/
 
 /*
  * This structure is used to chain together the ISRs for a particular
  * interrupt source (if it supports chaining).
  */
-typedef struct isr_node {
-    isrfunc	    isr;
-    int 	    pri;
-    void            *data;
-    char	    *name;
-    struct isr_node *next;
-} isr_node_t;
+typedef struct irq_node {
+	void		(*handler)(int, void *, struct pt_regs *);
+	unsigned long	flags;
+	void		*dev_id;
+	const char	*devname;
+	struct irq_node *next;
+} irq_node_t;
+
+/*
+ * This structure has only 4 elements for speed reasons
+ */
+typedef struct irq_handler {
+	void		(*handler)(int, void *, struct pt_regs *);
+	unsigned long	flags;
+	void		*dev_id;
+	const char	*devname;
+} irq_handler_t;
 
 /* count of spurious interrupts */
-extern volatile unsigned long num_spurious;
-
-/*
- * This function returns a new isr_node_t
- */
-extern isr_node_t *new_isr_node(void);
-
-/*
- * This function is used to add a specific interrupt service routine
- * for the specified interrupt source.
- *
- * If the source is machine specific, it will be passed along to the
- * machine specific routine.
- *
- * "data" is user specified data which will be passed to the isr routine.
- *
- * (isrfunc is defined in linux/config.h)
- */
-extern int add_isr (unsigned long source, isrfunc isr, int pri, void
-		    *data, char *name);
-
-/*
- * This routine will remove an isr for the specified interrupt source.
- */
-extern int remove_isr (unsigned long source, isrfunc isr, void *data);
-
-/*
- * This routine will insert an isr_node_t into a chain of nodes, using
- * the priority stored in the node.
- */
-extern void insert_isr (isr_node_t **listp, isr_node_t *node);
-
-/*
- * This routine will delete the isr node for isr from a chain of nodes
- */
-extern void delete_isr (isr_node_t **listp, isrfunc isr, void *data);
+extern volatile unsigned int num_spurious;
 
 /*
- * This routine may be used to call the isr routines in the passed list.
+ * This function returns a new irq_node_t
  */
-extern void call_isr_list (int irq, isr_node_t *p, struct pt_regs *fp);
+extern irq_node_t *new_irq_node(void);
 
 #endif /* _M68K_IRQ_H_ */
diff -urN pc-2.0.21/include/asm-m68k/machdep.h linux-2.0.21/include/asm-m68k/machdep.h
--- pc-2.0.21/include/asm-m68k/machdep.h	Mon May 20 06:54:29 1996
+++ linux-2.0.21/include/asm-m68k/machdep.h	Sat Aug 24 14:45:19 1996
@@ -8,24 +8,22 @@
 struct gendisk;
 struct buffer_head;
 
-#ifndef ISRFUNC_T
-typedef void (*isrfunc) (int irq, struct pt_regs *fp, void *data);
-#define ISRFUNC_T
-#endif /* ISRFUNC_T */
-
-extern void (*mach_sched_init)(isrfunc);
+extern void (*mach_sched_init) (void (*handler)(int, void *, struct pt_regs *));
+/* machine dependent keyboard functions */
 extern int (*mach_keyb_init) (void);
 extern int (*mach_kbdrate) (struct kbd_repeat *);
 extern void (*mach_kbd_leds) (unsigned int);
-extern void (*mach_init_INTS) (void);
-extern int (*mach_add_isr) (unsigned long source, isrfunc handler,
-			    int pri, void *data, char *name);
-extern int (*mach_remove_isr) (unsigned long source, isrfunc handler,
-			       void *data);
-extern int (*mach_get_irq_list)(char *buf, int len);
-extern void (*mach_process_int) (int level, struct pt_regs *fp);
-extern void (*mach_enable_irq) (unsigned);
-extern void (*mach_disable_irq) (unsigned);
+/* machine dependent irq functions */
+extern void (*mach_init_IRQ) (void);
+extern void (*(*mach_default_handler)[]) (int, void *, struct pt_regs *);
+extern int (*mach_request_irq) (unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
+                                unsigned long flags, const char *devname, void *dev_id);
+extern int (*mach_free_irq) (unsigned int irq, void *dev_id);
+extern void (*mach_enable_irq) (unsigned int irq);
+extern void (*mach_disable_irq) (unsigned int irq);
+extern int (*mach_get_irq_list) (char *buf);
+extern void (*mach_process_int) (int irq, struct pt_regs *fp);
+/* machine dependent timer functions */
 extern unsigned long (*mach_gettimeoffset)(void);
 extern void (*mach_gettod)(int *year, int *mon, int *day, int *hour,
 			   int *min, int *sec);
diff -urN pc-2.0.21/include/asm-m68k/page.h linux-2.0.21/include/asm-m68k/page.h
--- pc-2.0.21/include/asm-m68k/page.h	Sat Mar 23 21:26:13 1996
+++ linux-2.0.21/include/asm-m68k/page.h	Sat Aug 24 14:45:20 1996
@@ -50,10 +50,6 @@
 
 #endif
 
-/* m68k_is040or060 is != 0 for a '040 or higher; used numbers are 4 for 68040
- * and 6 for 68060 */
-extern int m68k_is040or060;
-
 /* This is the cache mode to be used for pages containing page descriptors for
  * processors >= '040. It is in pte_mknocache(), and the variable is defined
  * and initialized in head.S */
diff -urN pc-2.0.21/include/asm-m68k/pgtable.h linux-2.0.21/include/asm-m68k/pgtable.h
--- pc-2.0.21/include/asm-m68k/pgtable.h	Thu May 16 08:05:11 1996
+++ linux-2.0.21/include/asm-m68k/pgtable.h	Thu Sep 26 22:26:11 1996
@@ -1,6 +1,8 @@
 #ifndef _M68K_PGTABLE_H
 #define _M68K_PGTABLE_H
 
+#include<asm/setup.h>
+
 #ifndef __ASSEMBLY__
 
 /*
@@ -8,17 +10,20 @@
  * the m68k page table tree.
  */
 
-#define __flush_tlb() \
-do { 	\
-	if (m68k_is040or060) \
-		__asm__ __volatile__(".word 0xf510\n"::); /* pflushan */ \
-	else \
-		__asm__ __volatile__("pflusha\n"::); \
-} while (0)
+/*
+ * flush all atc entries (user-space entries only for the 680[46]0).
+ */
+static inline void __flush_tlb(void)
+{
+	if (CPU_IS_040_OR_060)
+		__asm__ __volatile__(".word 0xf510\n"::); /* pflushan */
+	else
+		__asm__ __volatile__("pflusha\n"::);
+}
 
 static inline void __flush_tlb_one(unsigned long addr)
 {
-	if (m68k_is040or060) {
+	if (CPU_IS_040_OR_060) {
 		register unsigned long a0 __asm__ ("a0") = addr;
 		__asm__ __volatile__(".word 0xf508" /* pflush (%a0) */
 				     : : "a" (a0));
@@ -27,7 +32,17 @@
 }
 
 #define flush_tlb() __flush_tlb()
-#define flush_tlb_all() flush_tlb()
+
+/*
+ * flush all atc entries (both kernel and user-space entries).
+ */
+static inline void flush_tlb_all(void)
+{
+	if (CPU_IS_040_OR_060)
+		__asm__ __volatile__(".word 0xf518\n"::); /* pflusha */
+	else
+		__asm__ __volatile__("pflusha\n"::);
+}
 
 static inline void flush_tlb_mm(struct mm_struct *mm)
 {
@@ -53,7 +68,12 @@
  * within a page table are directly modified.  Thus, the following
  * hook is made available.
  */
-#define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval))
+#define set_pte(pteptr, pteval) do{	\
+	((*(pteptr)) = (pteval));	\
+	if (CPU_IS_060)			\
+		__asm__ __volatile__(".word 0xf518\n"::); /* pflusha */ \
+	} while(0)
+
 
 /* PMD_SHIFT determines the size of the area a second-level page table can map */
 #define PMD_SHIFT	22
@@ -120,40 +140,49 @@
 #define _DESCTYPE_MASK	0x003
 
 #define _CACHEMASK040	(~0x060)
-#define _TABLE_MASK	(0xfffffff0)
+#define _TABLE_MASK	(0xfffffe00)
 
 #define _PAGE_TABLE	(_PAGE_SHORT)
 #define _PAGE_CHG_MASK  (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_NOCACHE)
 
 #ifndef __ASSEMBLY__
 
-#define PAGE_NONE	__pgprot(_PAGE_PRESENT | _PAGE_RONLY | _PAGE_ACCESSED | _PAGE_CACHE040)
-#define PAGE_SHARED	__pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_CACHE040)
-#define PAGE_COPY	__pgprot(_PAGE_PRESENT | _PAGE_RONLY | _PAGE_ACCESSED | _PAGE_CACHE040)
-#define PAGE_READONLY	__pgprot(_PAGE_PRESENT | _PAGE_RONLY | _PAGE_ACCESSED | _PAGE_CACHE040)
-#define PAGE_KERNEL	__pgprot(_PAGE_PRESENT | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_CACHE040)
+extern unsigned long mm_cachebits;
+
+#define PAGE_NONE	__pgprot(_PAGE_PRESENT | _PAGE_RONLY | _PAGE_ACCESSED | mm_cachebits)
+#define PAGE_SHARED	__pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | mm_cachebits)
+#define PAGE_COPY	__pgprot(_PAGE_PRESENT | _PAGE_RONLY | _PAGE_ACCESSED | mm_cachebits)
+#define PAGE_READONLY	__pgprot(_PAGE_PRESENT | _PAGE_RONLY | _PAGE_ACCESSED | mm_cachebits)
+#define PAGE_KERNEL	__pgprot(_PAGE_PRESENT | _PAGE_DIRTY | _PAGE_ACCESSED | mm_cachebits)
+
+/* Alternate definitions that are compile time constants, for
+   initializing protection_map.  The cachebits are fixed later.  */
+#define PAGE_NONE_C	__pgprot(_PAGE_PRESENT | _PAGE_RONLY | _PAGE_ACCESSED)
+#define PAGE_SHARED_C	__pgprot(_PAGE_PRESENT | _PAGE_ACCESSED)
+#define PAGE_COPY_C	__pgprot(_PAGE_PRESENT | _PAGE_RONLY | _PAGE_ACCESSED)
+#define PAGE_READONLY_C	__pgprot(_PAGE_PRESENT | _PAGE_RONLY | _PAGE_ACCESSED)
 
 /*
  * The m68k can't do page protection for execute, and considers that the same are read.
  * Also, write permissions imply read permissions. This is the closest we can get..
  */
-#define __P000	PAGE_NONE
-#define __P001	PAGE_READONLY
-#define __P010	PAGE_COPY
-#define __P011	PAGE_COPY
-#define __P100	PAGE_READONLY
-#define __P101	PAGE_READONLY
-#define __P110	PAGE_COPY
-#define __P111	PAGE_COPY
-
-#define __S000	PAGE_NONE
-#define __S001	PAGE_READONLY
-#define __S010	PAGE_SHARED
-#define __S011	PAGE_SHARED
-#define __S100	PAGE_READONLY
-#define __S101	PAGE_READONLY
-#define __S110	PAGE_SHARED
-#define __S111	PAGE_SHARED
+#define __P000	PAGE_NONE_C
+#define __P001	PAGE_READONLY_C
+#define __P010	PAGE_COPY_C
+#define __P011	PAGE_COPY_C
+#define __P100	PAGE_READONLY_C
+#define __P101	PAGE_READONLY_C
+#define __P110	PAGE_COPY_C
+#define __P111	PAGE_COPY_C
+
+#define __S000	PAGE_NONE_C
+#define __S001	PAGE_READONLY_C
+#define __S010	PAGE_SHARED_C
+#define __S011	PAGE_SHARED_C
+#define __S100	PAGE_READONLY_C
+#define __S101	PAGE_READONLY_C
+#define __S110	PAGE_SHARED_C
+#define __S111	PAGE_SHARED_C
 
 /* zero page used for uninitialized stuff */
 extern unsigned long empty_zero_page;
@@ -295,9 +324,8 @@
 	tsk->tss.crp[0] = 0x80000000 | _PAGE_SHORT;
 	tsk->tss.crp[1] = tsk->tss.pagedir_p;
 	if (tsk == current) {
-		if (m68k_is040or060)
-			__asm__ __volatile__ (".word 0xf510\n\t" /* pflushan */
-					      "movel %0@,%/d0\n\t"
+		if (CPU_IS_040_OR_060)
+			__asm__ __volatile__ ("movel %0@,%/d0\n\t"
 					      ".long 0x4e7b0806\n\t"
 					      /* movec d0,urp */
 					      : : "a" (&tsk->tss.crp[1])
@@ -349,11 +377,17 @@
 
 extern inline void nocache_page (unsigned long vaddr)
 {
-	if (m68k_is040or060) {
+	if (CPU_IS_040_OR_060) {
 		pgd_t *dir;
 		pmd_t *pmdp;
 		pte_t *ptep;
 
+		if(CPU_IS_060)
+			__asm__ __volatile__ ("movel %0,%/a0\n\t"
+					      ".word 0xf470"
+					      : : "g" (VTOP(vaddr))
+					      : "a0");
+
 		dir = pgd_offset_k(vaddr);
 		pmdp = pmd_offset(dir,vaddr);
 		ptep = pte_offset(pmdp,vaddr);
@@ -363,7 +397,7 @@
 
 static inline void cache_page (unsigned long vaddr)
 {
-	if (m68k_is040or060) {
+	if (CPU_IS_040_OR_060) {
 		pgd_t *dir;
 		pmd_t *pmdp;
 		pte_t *ptep;
@@ -514,10 +548,10 @@
 
 #define flush_icache() \
 do { \
-	if (m68k_is040or060) \
-		asm ("nop; .word 0xf498 /* cinva %%ic */"); \
+	if (CPU_IS_040_OR_060) \
+		asm __volatile__ ("nop; .word 0xf498 /* cinva %%ic */"); \
 	else \
-		asm ("movec %/cacr,%/d0;" \
+		asm __volatile__ ("movec %/cacr,%/d0;" \
 		     "oriw %0,%/d0;" \
 		     "movec %/d0,%/cacr" \
 		     : /* no outputs */ \
@@ -552,7 +586,7 @@
    process changes.  */
 #define __flush_cache_all()						\
     do {								\
-	if (m68k_is040or060)					        \
+	if (CPU_IS_040_OR_060)					        \
                __asm__ __volatile__ ("nop; .word 0xf478\n" ::);         \
         else                                                            \
 	       __asm__ __volatile__ ("movec %%cacr,%%d0\n\t"		\
@@ -563,7 +597,7 @@
 
 #define __flush_cache_030()						\
     do {								\
-	if (m68k_is040or060 == 0)					\
+	if (CPU_IS_020_OR_030)					\
 	       __asm__ __volatile__ ("movec %%cacr,%%d0\n\t"		\
 				     "orw %0,%%d0\n\t"			\
 				     "movec %%d0,%%cacr"		\
@@ -574,7 +608,11 @@
 
 extern inline void flush_cache_mm(struct mm_struct *mm)
 {
+#if FLUSH_VIRTUAL_CACHE_040
 	if (mm == current->mm) __flush_cache_all();
+#else
+	if (mm == current->mm) __flush_cache_030();
+#endif
 }
 
 extern inline void flush_cache_range(struct mm_struct *mm,
@@ -582,9 +620,11 @@
 				     unsigned long end)
 {
 	if (mm == current->mm){
-	    if (m68k_is040or060)
+#if FLUSH_VIRTUAL_CACHE_040
+	    if (CPU_IS_040_OR_060)
 	        cache_push_v(start, end-start);
 	    else
+#endif
 	        __flush_cache_030();
 	}
 }
@@ -593,9 +633,11 @@
 				    unsigned long vmaddr)
 {
 	if (vma->vm_mm == current->mm){
-	    if (m68k_is040or060)
+#if FLUSH_VIRTUAL_CACHE_040
+	    if (CPU_IS_040_OR_060)
 	        cache_push_v(vmaddr, PAGE_SIZE);
 	    else
+#endif
 	        __flush_cache_030();
 	}
 }
@@ -603,7 +645,7 @@
 /* Push the page at kernel virtual address and clear the icache */
 extern inline void flush_page_to_ram (unsigned long address)
 {
-    if (m68k_is040or060) {
+    if (CPU_IS_040_OR_060) {
 	register unsigned long tmp __asm ("a0") = VTOP(address);
 	__asm__ __volatile__ ("nop\n\t"
 			      ".word 0xf470 /* cpushp %%dc,(%0) */\n\t"
@@ -620,7 +662,7 @@
 /* Push n pages at kernel virtual address and clear the icache */
 extern inline void flush_pages_to_ram (unsigned long address, int n)
 {
-    if (m68k_is040or060) {
+    if (CPU_IS_040_OR_060) {
 	while (n--) {
 	    register unsigned long tmp __asm ("a0") = VTOP(address);
 	    __asm__ __volatile__ ("nop\n\t"
diff -urN pc-2.0.21/include/asm-m68k/posix_types.h linux-2.0.21/include/asm-m68k/posix_types.h
--- pc-2.0.21/include/asm-m68k/posix_types.h	Mon Jun  3 09:09:13 1996
+++ linux-2.0.21/include/asm-m68k/posix_types.h	Sat Aug 24 14:45:22 1996
@@ -1,5 +1,5 @@
-#ifndef __ARCH_I386_POSIX_TYPES_H
-#define __ARCH_I386_POSIX_TYPES_H
+#ifndef __ARCH_M68K_POSIX_TYPES_H
+#define __ARCH_M68K_POSIX_TYPES_H
 
 /*
  * This file is generally used by user-level software, so you need to
@@ -28,7 +28,11 @@
 #endif
 
 typedef struct {
+#if defined(__KERNEL__) || defined(__USE_ALL)
 	int	val[2];
+#else /* !defined(__KERNEL__) && !defined(__USE_ALL) */
+	int	__val[2];
+#endif /* !defined(__KERNEL__) && !defined(__USE_ALL) */
 } __kernel_fsid_t;
 
 #undef	__FD_SET
diff -urN pc-2.0.21/include/asm-m68k/processor.h linux-2.0.21/include/asm-m68k/processor.h
--- pc-2.0.21/include/asm-m68k/processor.h	Sun Mar 24 11:21:16 1996
+++ linux-2.0.21/include/asm-m68k/processor.h	Sat Sep 14 12:32:38 1996
@@ -30,7 +30,7 @@
 #define wp_works_ok 1
 
 /* MAX floating point unit state size (FSAVE/FRESTORE) */
-#define FPSTATESIZE   (216/sizeof(unsigned short))
+#define FPSTATESIZE   (216/sizeof(unsigned char))
 
 /* 
  * if you change this structure, you must change the code and offsets
@@ -48,7 +48,7 @@
 	unsigned long  esp0;		/* points to SR of stack frame */
 	unsigned long  fp[8*3];
 	unsigned long  fpcntl[3];	/* fp control regs */
-	unsigned short fpstate[FPSTATESIZE];  /* floating point state */
+	unsigned char  fpstate[FPSTATESIZE];  /* floating point state */
 };
 
 #define INIT_MMAP { &init_mm, 0, 0x40000000, __pgprot(_PAGE_PRESENT|_PAGE_ACCESSED), VM_READ | VM_WRITE | VM_EXEC }
@@ -59,7 +59,7 @@
 	NULL, 0, {0, 0}, 0 \
 }
 
-#define alloc_kernel_stack()    get_free_page(GFP_KERNEL)
+#define alloc_kernel_stack()    __get_free_page(GFP_KERNEL)
 #define free_kernel_stack(page) free_page((page))
 
 /*
diff -urN pc-2.0.21/include/asm-m68k/resource.h linux-2.0.21/include/asm-m68k/resource.h
--- pc-2.0.21/include/asm-m68k/resource.h	Fri Aug 23 14:30:15 1996
+++ linux-2.0.21/include/asm-m68k/resource.h	Sat Aug 24 14:45:23 1996
@@ -14,8 +14,9 @@
 #define RLIMIT_NPROC	6		/* max number of processes */
 #define RLIMIT_NOFILE	7		/* max number of open files */
 #define RLIMIT_MEMLOCK	8		/* max locked-in-memory address space*/
+#define RLIMIT_AS	9		/* address space limit */
 
-#define RLIM_NLIMITS	9
+#define RLIM_NLIMITS	10
 
 #ifdef __KERNEL__
 
@@ -29,6 +30,7 @@
   {LONG_MAX, LONG_MAX}, \
   {MAX_TASKS_PER_USER, MAX_TASKS_PER_USER}, \
   {NR_OPEN, NR_OPEN},   \
+  {LONG_MAX, LONG_MAX}, \
   {LONG_MAX, LONG_MAX}  \
 }
 
diff -urN pc-2.0.21/include/asm-m68k/segment.h linux-2.0.21/include/asm-m68k/segment.h
--- pc-2.0.21/include/asm-m68k/segment.h	Thu May 16 08:05:11 1996
+++ linux-2.0.21/include/asm-m68k/segment.h	Sat Aug 24 14:45:24 1996
@@ -138,18 +138,18 @@
 	tmp = n;
 	n >>= 2;
 	if (n != 0)
-		__asm__ ("1:\t"
+		__asm__ __volatile__ ("1:\t"
 			 "movel %1@+,%/d0\n\t"
 			 "movesl %/d0,%2@+\n\t"
-			      "dbra %0,1b\n\t"
-			      "clrw %0\n\t"
-			      "subql #1,%0\n\t"
+			 "dbra %0,1b\n\t"
+			 "clrw %0\n\t"
+			 "subql #1,%0\n\t"
 			 "bccs 1b\n\t"
-			      : "=d" (n), "=a" (from), "=a" (to)
+			 : "=d" (n), "=a" (from), "=a" (to)
 			 : "0" (n-1), "1" (from), "2" (to)
 			 : "d0", "memory");
 	if (tmp & 2)
-		__asm__ ("movew %0@+,%/d0\n\t"
+		__asm__ __volatile__ ("movew %0@+,%/d0\n\t"
 			 "movesw %/d0,%1@+\n\t"
 			 : "=a" (from), "=a" (to)
 			 : "0" (from), "1" (to)
@@ -159,7 +159,7 @@
 			 "movesb %/d0,%1@\n\t"
 			 : /* no outputs */
 			 : "a" (from), "a" (to)
-			      : "d0", "memory");
+			 : "d0", "memory");
 }
 
 static inline void __constant_memcpy_tofs(void * to, const void * from, unsigned long n)
@@ -234,18 +234,18 @@
 	tmp = n;
 	n >>= 2;
 	if (n != 0)
-		__asm__ ("1:\t"
+		__asm__ __volatile__ ("1:\t"
 			 "movesl %1@+,%/d0\n\t"
 			 "movel %/d0,%2@+\n\t"
-			      "dbra %0,1b\n\t"
-			      "clrw %0\n\t"
-			      "subql #1,%0\n\t"
+			 "dbra %0,1b\n\t"
+			 "clrw %0\n\t"
+			 "subql #1,%0\n\t"
 			 "bccs 1b\n\t"
-			      : "=d" (n), "=a" (from), "=a" (to)
+			 : "=d" (n), "=a" (from), "=a" (to)
 			 : "0" (n-1), "1" (from), "2" (to)
 			 : "d0", "memory");
 	if (tmp & 2)
-		__asm__ ("movesw %0@+,%/d0\n\t"
+		__asm__ __volatile__ ("movesw %0@+,%/d0\n\t"
 			 "movew %/d0,%1@+\n\t"
 			 : "=a" (from), "=a" (to)
 			 : "0" (from), "1" (to)
@@ -255,7 +255,7 @@
 			 "moveb %/d0,%1@\n\t"
 			 : /* no outputs */
 			 : "a" (from), "a" (to)
-			      : "d0", "memory");
+			 : "d0", "memory");
 }
 
 static inline void __constant_memcpy_fromfs(void * to, const void * from, unsigned long n)
diff -urN pc-2.0.21/include/asm-m68k/semaphore.h linux-2.0.21/include/asm-m68k/semaphore.h
--- pc-2.0.21/include/asm-m68k/semaphore.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/include/asm-m68k/semaphore.h	Wed Sep 18 19:06:46 1996
@@ -0,0 +1,68 @@
+#ifndef _M68K_SEMAPHORE_H
+#define _M68K_SEMAPHORE_H
+
+#include <linux/linkage.h>
+
+/*
+ * SMP- and interrupt-safe semaphores..
+ *
+ * (C) Copyright 1996 Linus Torvalds
+ *
+ * m68k version by Andreas Schwab
+ */
+
+struct semaphore {
+	int count;
+	int waiting;
+	struct wait_queue * wait;
+};
+
+#define MUTEX ((struct semaphore) { 1, 0, NULL })
+#define MUTEX_LOCKED ((struct semaphore) { 0, 0, NULL })
+
+asmlinkage void down_failed(void /* special register calling convention */);
+asmlinkage void up_wakeup(void /* special register calling convention */);
+
+extern void __down(struct semaphore * sem);
+extern void __up(struct semaphore * sem);
+
+/*
+ * This is ugly, but we want the default case to fall through.
+ * "down_failed" is a special asm handler that calls the C
+ * routine that actually waits. See arch/m68k/lib/semaphore.S
+ */
+extern inline void down(struct semaphore * sem)
+{
+	register struct semaphore *sem1 __asm__ ("%a1") = sem;
+	__asm__ __volatile__(
+		"# atomic down operation\n"
+		"1:\n\t"
+		"lea %%pc@(1b),%%a0\n\t"
+		"subql #1,%0\n\t"
+		"jmi " SYMBOL_NAME_STR(down_failed)
+		: /* no outputs */
+		: "m" (sem->count), "a" (sem1)
+		: "%a0", "%d0", "%d1", "memory");
+}
+
+/*
+ * Note! This is subtle. We jump to wake people up only if
+ * the semaphore was negative (== somebody was waiting on it).
+ * The default case (no contention) will result in NO
+ * jumps for both down() and up().
+ */
+extern inline void up(struct semaphore * sem)
+{
+	register struct semaphore *sem1 __asm__ ("%a1") = sem;
+	__asm__ __volatile__(
+		"# atomic up operation\n\t"
+		"lea %%pc@(1f),%%a0\n\t"
+		"addql #1,%0\n\t"
+		"jls " SYMBOL_NAME_STR(up_wakeup) "\n"
+		"1:"
+		: /* no outputs */
+		: "m" (sem->count), "a" (sem1)
+		: "%a0", "%d0", "%d1", "memory");
+}
+
+#endif
diff -urN pc-2.0.21/include/asm-m68k/setup.h linux-2.0.21/include/asm-m68k/setup.h
--- pc-2.0.21/include/asm-m68k/setup.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/include/asm-m68k/setup.h	Thu Sep 26 22:26:11 1996
@@ -0,0 +1,397 @@
+/*
+** asm/setup.h -- Definition of the Linux/m68k boot information structure
+**
+** Copyright 1992 by Greg Harp
+**
+** 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.
+**
+** Created 09/29/92 by Greg Harp
+**
+** 5/2/94 Roman Hodek:
+**   Added bi_atari part of the machine dependent union bi_un; for now it
+**   contains just a model field to distinguish between TT and Falcon.
+** 26/7/96 Roman Zippel:
+**   Renamed to setup.h; added some useful macros to allow gcc some
+**   optimizations if possible.
+*/
+
+#ifndef _M68K_SETUP_H
+#define _M68K_SETUP_H
+
+#include <linux/config.h>
+#include <asm/zorro.h>
+
+/*
+ * Amiga specific part of bootinfo structure.
+ */
+
+#define NUM_AUTO    16
+
+#ifndef __ASSEMBLY__
+
+#define AMIGAHW_DECLARE(name)	unsigned name : 1
+#define AMIGAHW_SET(name)	(boot_info.bi_amiga.hw_present.name = 1)
+#define AMIGAHW_PRESENT(name)	(boot_info.bi_amiga.hw_present.name)
+
+struct bi_Amiga {
+	int model;				/* Amiga Model (3000?) */
+	int num_autocon;			/* # of autoconfig devices found */
+	struct ConfigDev autocon[NUM_AUTO];	/* up to 16 autoconfig devices */
+#ifdef HACKER_KERNEL
+	void (*exit_func)(void);		/* addr of function to exit kernel */
+	unsigned long chip_addr;		/* start of chip memory (bytes) */
+#endif
+	unsigned long chip_size;		/* size of chip memory (bytes) */
+	unsigned char vblank;	 		/* VBLANK frequency */
+	unsigned char psfreq; 			/* power supply frequency */
+	unsigned long eclock; 			/* EClock frequency */
+	unsigned long chipset;			/* native chipset present */
+	struct {
+		/* video hardware */
+		AMIGAHW_DECLARE(AMI_VIDEO);	/* Amiga Video */
+		AMIGAHW_DECLARE(AMI_BLITTER);	/* Amiga Blitter */
+		AMIGAHW_DECLARE(AMBER_FF);	/* Amber Flicker Fixer */
+		/* sound hardware */
+		AMIGAHW_DECLARE(AMI_AUDIO);	/* Amiga Audio */
+		/* disk storage interfaces */
+		AMIGAHW_DECLARE(AMI_FLOPPY);	/* Amiga Floppy */
+		AMIGAHW_DECLARE(A3000_SCSI);	/* SCSI (wd33c93, A3000 alike) */
+		AMIGAHW_DECLARE(A4000_SCSI);	/* SCSI (ncr53c710, A4000T alike) */
+		AMIGAHW_DECLARE(A1200_IDE);	/* IDE (A1200 alike) */
+		AMIGAHW_DECLARE(A4000_IDE);	/* IDE (A4000 alike) */
+		AMIGAHW_DECLARE(CD_ROM);	/* CD ROM drive */
+		/* other I/O hardware */
+		AMIGAHW_DECLARE(AMI_KEYBOARD);	/* Amiga Keyboard */
+		AMIGAHW_DECLARE(AMI_MOUSE);	/* Amiga Mouse */
+		AMIGAHW_DECLARE(AMI_SERIAL);	/* Amiga Serial */
+		AMIGAHW_DECLARE(AMI_PARALLEL);	/* Amiga Parallel */
+		/* real time clocks */
+		AMIGAHW_DECLARE(A2000_CLK);	/* Hardware Clock (A2000 alike) */
+		AMIGAHW_DECLARE(A3000_CLK);	/* Hardware Clock (A3000 alike) */
+		/* supporting hardware */
+		AMIGAHW_DECLARE(CHIP_RAM);	/* Chip RAM */
+		AMIGAHW_DECLARE(PAULA);		/* Paula (8364) */
+		AMIGAHW_DECLARE(DENISE);	/* Denise (8362) */
+		AMIGAHW_DECLARE(DENISE_HR);	/* Denise (8373) */
+		AMIGAHW_DECLARE(LISA);		/* Lisa (8375) */
+		AMIGAHW_DECLARE(AGNUS_PAL);	/* Normal/Fat PAL Agnus (8367/8371) */
+		AMIGAHW_DECLARE(AGNUS_NTSC);	/* Normal/Fat NTSC Agnus (8361/8370) */
+		AMIGAHW_DECLARE(AGNUS_HR_PAL);	/* Fat Hires PAL Agnus (8372) */
+		AMIGAHW_DECLARE(AGNUS_HR_NTSC);	/* Fat Hires NTSC Agnus (8372) */
+		AMIGAHW_DECLARE(ALICE_PAL);	/* PAL Alice (8374) */
+		AMIGAHW_DECLARE(ALICE_NTSC);	/* NTSC Alice (8374) */
+		AMIGAHW_DECLARE(MAGIC_REKICK);	/* A3000 Magic Hard Rekick */
+		AMIGAHW_DECLARE(ZORRO);		/* Zorro AutoConfig */
+	} hw_present;
+};
+
+#else	/* __ASSEMBLY__ */
+ 
+BI_amiga_model		= BI_un
+BI_amiga_num_autcon	= BI_amiga_model+4
+BI_amiga_autocon	= BI_amiga_num_autcon+4
+#ifdef HACKER_KERNEL
+BI_amiga_exit_func	= BI_amiga_autocon+(CD_sizeof*NUM_AUTO)
+BI_amiga_chip_addr	= BI_amiga_exit_func+4
+BI_amiga_chip_size	= BI_amiga_chip_addr+4
+#else
+BI_amiga_chip_size	= BI_amiga_autocon+(CD_sizeof*NUM_AUTO)
+#endif
+BI_amiga_vblank		= BI_amiga_chip_size+4
+BI_amiga_psfreq		= BI_amiga_vblank+1
+BI_amiga_eclock		= BI_amiga_psfreq+1
+BI_amiga_chipset	= BI_amiga_eclock+4
+BI_amiga_hw_present	= BI_amiga_chipset+4
+
+#endif	/* __ASSEMBLY__ */
+
+/* Atari specific part of bootinfo */
+
+/*
+ * Define several Hardware-Chips for indication so that for the ATARI we do
+ * no longer decide whether it is a Falcon or other machine . It's just
+ * important what hardware the machine uses
+ */
+
+/* ++roman 08/08/95: rewritten from ORing constants to a C bitfield */
+
+#ifndef __ASSEMBLY__
+
+#define ATARIHW_DECLARE(name)	unsigned name : 1
+#define ATARIHW_SET(name)	(boot_info.bi_atari.hw_present.name = 1)
+#define ATARIHW_PRESENT(name)	(boot_info.bi_atari.hw_present.name)
+
+struct bi_Atari {
+	struct {
+		/* video hardware */
+		ATARIHW_DECLARE(STND_SHIFTER);	/* ST-Shifter - no base low ! */
+		ATARIHW_DECLARE(EXTD_SHIFTER);	/* STe-Shifter - 24 bit address */
+		ATARIHW_DECLARE(TT_SHIFTER);	/* TT-Shifter */
+		ATARIHW_DECLARE(VIDEL_SHIFTER);	/* Falcon-Shifter */
+		/* sound hardware */
+		ATARIHW_DECLARE(YM_2149);	/* Yamaha YM 2149 */
+		ATARIHW_DECLARE(PCM_8BIT);	/* PCM-Sound in STe-ATARI */
+		ATARIHW_DECLARE(CODEC);		/* CODEC Sound (Falcon) */
+		/* disk storage interfaces */
+		ATARIHW_DECLARE(TT_SCSI);	/* Directly mapped NCR5380 */
+		ATARIHW_DECLARE(ST_SCSI);	/* NCR5380 via ST-DMA (Falcon) */
+		ATARIHW_DECLARE(ACSI);		/* Standard ACSI like in STs */
+		ATARIHW_DECLARE(IDE);		/* IDE Interface */
+		ATARIHW_DECLARE(FDCSPEED);	/* 8/16 MHz switch for FDC */
+		/* other I/O hardware */
+		ATARIHW_DECLARE(ST_MFP);	/* The ST-MFP (there should
+						   be no Atari without
+						   it... but who knows?) */
+		ATARIHW_DECLARE(TT_MFP);	/* 2nd MFP */
+		ATARIHW_DECLARE(SCC);		/* Serial Communications Contr. */
+		ATARIHW_DECLARE(ST_ESCC);	/* SCC Z83230 in an ST */
+		ATARIHW_DECLARE(ANALOG_JOY);	/* Paddle Interface for STe
+						   and Falcon */
+		ATARIHW_DECLARE(MICROWIRE);	/* Microwire Interface */
+		/* DMA */
+		ATARIHW_DECLARE(STND_DMA);	/* 24 Bit limited ST-DMA */
+		ATARIHW_DECLARE(EXTD_DMA);	/* 32 Bit ST-DMA */
+		ATARIHW_DECLARE(SCSI_DMA);	/* DMA for the NCR5380 */
+		ATARIHW_DECLARE(SCC_DMA);	/* DMA for the SCC */
+		/* real time clocks */
+		ATARIHW_DECLARE(TT_CLK);	/* TT compatible clock chip */
+		ATARIHW_DECLARE(MSTE_CLK);	/* Mega ST(E) clock chip */
+		/* supporting hardware */
+		ATARIHW_DECLARE(SCU);		/* System Control Unit */
+		ATARIHW_DECLARE(BLITTER);	/* Blitter */
+		ATARIHW_DECLARE(VME);		/* VME Bus */
+	} hw_present;
+	unsigned long mch_cookie;		/* _MCH cookie from TOS */
+};
+
+/* mch_cookie values (upper word) */
+#define	ATARI_MCH_ST		0
+#define	ATARI_MCH_STE		1
+#define	ATARI_MCH_TT		2
+#define	ATARI_MCH_FALCON	3
+
+struct mem_info {
+	unsigned long addr;			/* physical address of memory chunk */
+	unsigned long size;			/* length of memory chunk (in bytes) */
+};
+
+#else	/* __ASSEMBLY__ */
+
+MI_addr		= 0
+MI_size		= MI_addr+4
+MI_sizeof	= MI_size+4
+
+#endif /* __ASSEMBLY__ */
+
+#define NUM_MEMINFO  4
+
+#define MACH_AMIGA   1
+#define MACH_ATARI   2
+#define MACH_MAC     3
+
+/*
+ * CPU and FPU types
+ */
+
+#define CPUB_68020 0
+#define CPUB_68030 1
+#define CPUB_68040 2
+#define CPUB_68060 3
+#define FPUB_68881 5
+#define FPUB_68882 6
+#define FPUB_68040 7				/* Internal FPU */
+#define FPUB_68060 8				/* Internal FPU */
+
+#define CPU_68020    (1<<CPUB_68020)
+#define CPU_68030    (1<<CPUB_68030)
+#define CPU_68040    (1<<CPUB_68040)
+#define CPU_68060    (1<<CPUB_68060)
+#define CPU_MASK     (31)
+#define FPU_68881    (1<<FPUB_68881)
+#define FPU_68882    (1<<FPUB_68882)
+#define FPU_68040    (1<<FPUB_68040)		/* Internal FPU */
+#define FPU_68060    (1<<FPUB_68060)		/* Internal FPU */
+#define FPU_MASK     (0xfe0)
+
+#define CL_SIZE      (256)
+
+/*
+ * machine type definitions
+ */
+
+#if !defined(CONFIG_AMIGA)
+#  define MACH_IS_AMIGA (0)
+#elif defined(CONFIG_ATARI) || defined(CONFIG_MAC)
+#  define MACH_IS_AMIGA (boot_info.machtype == MACH_AMIGA)
+#else
+#  define CONFIG_AMIGA_ONLY
+#  define MACH_IS_AMIGA (1)
+#  define MACH_TYPE (MACH_AMIGA)
+#endif
+
+#if !defined(CONFIG_ATARI)
+#  define MACH_IS_ATARI (0)
+#elif defined(CONFIG_AMIGA) || defined(CONFIG_MAC)
+#  define MACH_IS_ATARI (boot_info.machtype == MACH_ATARI)
+#else
+#  define CONFIG_ATARI_ONLY
+#  define MACH_IS_ATARI (1)
+#  define MACH_TYPE (MACH_ATARI)
+#endif
+
+#if defined(CONFIG_MAC)
+#  error Currently no Mac support!
+#endif
+
+#ifndef MACH_TYPE
+#  define MACH_TYPE (boot_info.machtype)
+#endif
+
+/*
+ * cpu type definitions
+ */
+
+#if !defined(CONFIG_M68020)
+#  define CPU_IS_020 (0)
+#elif defined(CONFIG_M68030) || defined(CONFIG_M68040) || defined(CONFIG_M68060)
+#  define CPU_IS_020 (boot_info.cputype & CPU_68020)
+#else
+#  define CONFIG_M68020_ONLY
+#  define CPU_IS_020 (1)
+#endif
+
+#if !defined(CONFIG_M68030)
+#  define CPU_IS_030 (0)
+#elif defined(CONFIG_M68020) || defined(CONFIG_M68040) || defined(CONFIG_M68060)
+#  define CPU_IS_030 (boot_info.cputype & CPU_68030)
+#else
+#  define CONFIG_M68030_ONLY
+#  define CPU_IS_030 (1)
+#endif
+
+#if !defined(CONFIG_M68040)
+#  define CPU_IS_040 (0)
+#elif defined(CONFIG_M68020) || defined(CONFIG_M68030) || defined(CONFIG_M68060)
+#  define CPU_IS_040 (boot_info.cputype & CPU_68040)
+#else
+#  define CONFIG_M68040_ONLY
+#  define CPU_IS_040 (1)
+#endif
+
+#if !defined(CONFIG_M68060)
+#  define CPU_IS_060 (0)
+#elif defined(CONFIG_M68020) || defined(CONFIG_M68030) || defined(CONFIG_M68040)
+#  define CPU_IS_060 (boot_info.cputype & CPU_68060)
+#else
+#  define CONFIG_M68060_ONLY
+#  define CPU_IS_060 (1)
+#endif
+
+#if !defined(CONFIG_M68020) && !defined(CONFIG_M68030)
+#  define CPU_IS_020_OR_030 (0)
+#else
+#  define CONFIG_M68020_OR_M68030
+#  if defined(CONFIG_M68040) || defined(CONFIG_M68060)
+#    define CPU_IS_020_OR_030 (!m68k_is040or060)
+#  else
+#    define CONFIG_M68020_OR_M68030_ONLY
+#    define CPU_IS_020_OR_030 (1)
+#  endif
+#endif
+
+#if !defined(CONFIG_M68040) && !defined(CONFIG_M68060)
+#  define CPU_IS_040_OR_060 (0)
+#else
+#  define CONFIG_M68040_OR_M68060
+#  if defined(CONFIG_M68020) || defined(CONFIG_M68030)
+#    define CPU_IS_040_OR_060 (m68k_is040or060)
+#  else
+#    define CONFIG_M68040_OR_M68060_ONLY
+#    define CPU_IS_040_OR_060 (1)
+#  endif
+#endif
+
+#define CPU_TYPE (boot_info.cputype)
+
+#ifndef __ASSEMBLY__
+#ifdef __KERNEL__
+	/*
+	 * m68k_is040or060 is != 0 for a '040 or higher;
+	 * used numbers are 4 for 68040 and 6 for 68060.
+	 */
+
+extern int m68k_is040or060;
+#endif
+
+struct bootinfo {
+	unsigned long machtype;			/* machine type */
+	unsigned long cputype;			/* system CPU & FPU */
+	struct mem_info memory[NUM_MEMINFO];	/* memory description */
+	int num_memory;				/* # of memory blocks found */
+	unsigned long ramdisk_size;		/* ramdisk size in 1024 byte blocks */
+	unsigned long ramdisk_addr;		/* address of the ram disk in mem */
+	char command_line[CL_SIZE];		/* kernel command line parameters */
+	union {
+		struct bi_Amiga bi_ami; 	/* Amiga specific information */
+		struct bi_Atari bi_ata; 	/* Atari specific information */
+	} bi_un;
+};
+#define bi_amiga bi_un.bi_ami
+#define bi_atari bi_un.bi_ata
+#define bi_mac	 bi_un.bi_mac
+
+extern struct bootinfo
+    boot_info;
+
+#else	/* __ASSEMBLY__ */
+
+BI_machtype	= 0
+BI_cputype	= BI_machtype+4
+BI_memory	= BI_cputype+4
+BI_num_memory	= BI_memory+(MI_sizeof*NUM_MEMINFO)
+BI_ramdisk_size	= BI_num_memory+4
+BI_ramdisk_addr	= BI_ramdisk_size+4
+BI_command_line	= BI_ramdisk_addr+4
+BI_un		= BI_command_line+CL_SIZE
+
+#endif /* __ASSEMBLY__ */
+
+
+/*
+ * Stuff for bootinfo interface versioning
+ *
+ * At the start of kernel code, a 'struct bootversion' is located. bootstrap
+ * checks for a matching version of the interface before booting a kernel, to
+ * avoid user confusion if kernel and bootstrap don't work together :-)
+ *
+ * If incompatible changes are made to the bootinfo interface, the major
+ * number below should be stepped (and the minor reset to 0) for the
+ * appropriate machine. If a change is backward-compatible, the minor should
+ * be stepped. "Backwards-compatible" means that booting will work, but
+ * certain features may not.
+ */
+
+#define BOOTINFOV_MAGIC			0x4249561A	/* 'BIV^Z' */
+#define MK_BI_VERSION(major,minor)	(((major)<<16)+(minor))
+#define BI_VERSION_MAJOR(v)		(((v) >> 16) & 0xffff)
+#define BI_VERSION_MINOR(v)		((v) & 0xffff)
+
+#ifndef __ASSEMBLY__
+
+struct bootversion {
+	unsigned short branch;
+	unsigned long magic;
+	struct {
+		unsigned long machtype;
+		unsigned long version;
+	} machversions[0];
+};
+
+#endif /* __ASSEMBLY__ */
+
+#define AMIGA_BOOTI_VERSION    MK_BI_VERSION( 1, 0 )
+#define ATARI_BOOTI_VERSION    MK_BI_VERSION( 1, 0 )
+
+#endif /* _M68K_SETUP_H */
diff -urN pc-2.0.21/include/asm-m68k/string.h linux-2.0.21/include/asm-m68k/string.h
--- pc-2.0.21/include/asm-m68k/string.h	Sat Mar 30 13:11:50 1996
+++ linux-2.0.21/include/asm-m68k/string.h	Thu Sep 26 22:25:13 1996
@@ -1,6 +1,9 @@
 #ifndef _M68K_STRING_H_
 #define _M68K_STRING_H_
 
+#include <linux/config.h>
+#include <asm/page.h>
+
 #define __HAVE_ARCH_STRCPY
 extern inline char * strcpy(char * dest,const char *src)
 {
@@ -187,14 +190,65 @@
 }
 
 #define __HAVE_ARCH_MEMSET
-extern inline void * memset(void * s,int c,size_t count)
+/*
+ * This is really ugly, but its highly optimizatiable by the
+ * compiler and is meant as compensation for gcc's missing
+ * __builtin_memset(). For the 680[23]0	it might be worth considering
+ * the optimal number of misaligned writes compared to the number of
+ * tests'n'branches needed to align the destination address. The
+ * 680[46]0 doesn't really care due to their copy-back caches.
+ *						10/09/96 - Jes Sorensen
+ */
+extern inline void * __memset_g(void * s, int c, size_t count)
 {
   void *xs = s;
   size_t temp;
 
   if (!count)
     return xs;
+
   c &= 0xff;
+  c |= c << 8;
+  c |= c << 16;
+
+  if (count < 36){
+	  long *ls = s;
+
+	  switch(count){
+	  case 32: case 33: case 34: case 35:
+		  *ls++ = c;
+	  case 28: case 29: case 30: case 31:
+		  *ls++ = c;
+	  case 24: case 25: case 26: case 27:
+		  *ls++ = c;
+	  case 20: case 21: case 22: case 23:
+		  *ls++ = c;
+	  case 16: case 17: case 18: case 19:
+		  *ls++ = c;
+	  case 12: case 13: case 14: case 15:
+		  *ls++ = c;
+	  case 8: case 9: case 10: case 11:
+		  *ls++ = c;
+	  case 4: case 5: case 6: case 7:
+		  *ls++ = c;
+		  break;
+	  default:
+		  break;
+	  }
+	  s = ls;
+	  if (count & 0x02){
+		  short *ss = s;
+		  *ss++ = c;
+		  s = ss;
+	  }
+	  if (count & 0x01){
+		  char *cs = s;
+		  *cs++ = c;
+		  s = cs;
+	  }
+	  return xs;
+  }
+
   if ((long) s & 1)
     {
       char *cs = s;
@@ -202,7 +256,6 @@
       s = cs;
       count--;
     }
-  c |= c << 8;
   if (count > 2 && (long) s & 2)
     {
       short *ss = s;
@@ -214,7 +267,6 @@
   if (temp)
     {
       long *ls = s;
-      c |= c << 16;
       temp--;
       do
 	*ls++ = c;
@@ -235,10 +287,141 @@
   return xs;
 }
 
+/*
+ * __memset_page assumes that data is longword aligned. Most, if not
+ * all, of these page sized memsets are performed on page aligned
+ * areas, thus we do not need to check if the destination is longword
+ * aligned. Of course we suffer a serious performance loss if this is
+ * not the case but I think the risk of this ever happening is
+ * extremely small. We spend a lot of time clearing pages in
+ * get_empty_page() so I think it is worth it anyway. Besides, the
+ * 680[46]0 do not really care about misaligned writes due to their
+ * copy-back cache.
+ *
+ * The optimized case for the 680[46]0 is implemented using the move16
+ * instruction. My tests showed that this implementation is 35-45%
+ * faster than the original implementation using movel, the only
+ * caveat is that the destination address must be 16-byte aligned.
+ *                                            01/09/96 - Jes Sorensen
+ */
+extern inline void * __memset_page(void * s,int c,size_t count)
+{
+  unsigned long data, tmp;
+  void *xs, *sp;
+
+  xs = sp = s;
+
+  c = c & 255;
+  data = c | (c << 8);
+  data |= data << 16;
+
+#if defined(CONFIG_OPTIMIZE_040) || defined(CONFIG_OPTIMIZE_060)
+
+  if (((unsigned long) s) & 0x0f)
+	  memset(s, c, count);
+  else{
+	  *((unsigned long *)(s))++ = data;
+	  *((unsigned long *)(s))++ = data;
+	  *((unsigned long *)(s))++ = data;
+	  *((unsigned long *)(s))++ = data;
+
+	  __asm__ __volatile__("1:\t"
+			       "move16 %2@+,%0@+\n\t"
+			       "subqw  #8,%2\n\t"
+			       "subqw  #8,%2\n\t"
+			       "dbra   %1,1b\n\t"
+			       : "=a" (s), "=d" (tmp)
+			       : "a" (sp), "0" (s), "1" ((count - 16) / 16 - 1)
+			       );
+  }
+
+#else
+  __asm__ __volatile__("1:\t"
+		       "movel %2,%0@+\n\t"
+		       "movel %2,%0@+\n\t"
+		       "movel %2,%0@+\n\t"
+		       "movel %2,%0@+\n\t"
+		       "movel %2,%0@+\n\t"
+		       "movel %2,%0@+\n\t"
+		       "movel %2,%0@+\n\t"
+		       "movel %2,%0@+\n\t"
+		       "dbra  %1,1b\n\t"
+		       : "=a" (s), "=d" (tmp)
+		       : "d" (data), "0" (s), "1" (count / 32 - 1)
+		       );
+#endif
+
+  return xs;
+}
+
+#define __memset_const(s,c,count) \
+((count==PAGE_SIZE) ? \
+  __memset_page((s),(c),(count)) : \
+  __memset_g((s),(c),(count)))
+
+#define memset(s, c, count) \
+(__builtin_constant_p(count) ? \
+ __memset_const((s),(c),(count)) : \
+ memset((s),(c),(count)))
+
 #define __HAVE_ARCH_MEMCPY
+/*
+ * __builtin_memcpy() does not handle page-sized memcpys very well,
+ * thus following the same assumptions as for page-sized memsets, this
+ * function copies page-sized areas using an unrolled loop, without
+ * considering alignment.
+ *
+ * For the 680[46]0 only kernels we use the move16 instruction instead
+ * as it writes through the data-cache, invalidating the cache-lines
+ * touched. In this way we do not use up the entire data-cache (well,
+ * half of it on the 68060) by copying a page. An unrolled loop of two
+ * move16 instructions seem to the fastest. The only caveat is that
+ * both source and destination must be 16-byte aligned, if not we fall
+ * back to the generic memcpy function.  - Jes
+ */
+extern inline void * __memcpy_page(void * to, const void * from, size_t count)
+{
+  unsigned long tmp;
+  void *xto = to;
+
+#if defined(CONFIG_OPTIMIZE_040) || defined(CONFIG_OPTIMIZE_060)
+
+  if (((unsigned long) to | (unsigned long) from) & 0x0f)
+	  return memcpy(to, from, count);
+
+  __asm__ __volatile__("1:\t"
+		       "move16 %1@+,%0@+\n\t"
+		       "move16 %1@+,%0@+\n\t"
+		       "dbra  %2,1b\n\t"
+		       : "=a" (to), "=a" (from), "=d" (tmp)
+		       : "0" (to), "1" (from) , "2" (count / 32 - 1)
+		       );
+#else
+  __asm__ __volatile__("1:\t"
+		       "movel %1@+,%0@+\n\t"
+		       "movel %1@+,%0@+\n\t"
+		       "movel %1@+,%0@+\n\t"
+		       "movel %1@+,%0@+\n\t"
+		       "movel %1@+,%0@+\n\t"
+		       "movel %1@+,%0@+\n\t"
+		       "movel %1@+,%0@+\n\t"
+		       "movel %1@+,%0@+\n\t"
+		       "dbra  %2,1b\n\t"
+		       : "=a" (to), "=a" (from), "=d" (tmp)
+		       : "0" (to), "1" (from) , "2" (count / 32 - 1)
+		       );
+#endif
+  return xto;
+}
+
+#define __memcpy_const(to, from, n) \
+((n==PAGE_SIZE) ? \
+  __memcpy_page((to),(from),(n)) : \
+  __builtin_memcpy((to),(from),(n)))
+
 #define memcpy(to, from, n) \
 (__builtin_constant_p(n) ? \
- __builtin_memcpy((to),(from),(n)) : \
+ __memcpy_const((to),(from),(n)) : \
  memcpy((to),(from),(n)))
 
 #define __HAVE_ARCH_MEMMOVE
diff -urN pc-2.0.21/include/asm-m68k/system.h linux-2.0.21/include/asm-m68k/system.h
--- pc-2.0.21/include/asm-m68k/system.h	Mon May  6 11:44:32 1996
+++ linux-2.0.21/include/asm-m68k/system.h	Thu Sep 26 22:25:05 1996
@@ -40,13 +40,20 @@
  * to push them onto the stack and read them back right after.
  *
  * 02/17/96 - Jes Sorensen (jds@kom.auc.dk)
+ *
+ * Changed 96/09/19 by Andreas Schwab
+ * pass prev in a0, next in a1, offset of tss in d1, and whether
+ * the mm structures are shared in d2 (to avoid atc flushing).
  */
 asmlinkage void resume(void);
 #define switch_to(prev,next) { \
-  register int k __asm__ ("a1") = (int)&((struct task_struct *)0)->tss; \
-  register int n __asm__ ("d1") = (int)next; \
+  register void *_prev __asm__ ("a0") = (prev); \
+  register void *_next __asm__ ("a1") = (next); \
+  register int _tssoff __asm__ ("d1") = (int)&((struct task_struct *)0)->tss; \
+  register char _shared __asm__ ("d2") = ((prev)->mm == (next)->mm); \
   __asm__ __volatile__("jbsr " SYMBOL_NAME_STR(resume) "\n\t" \
-		       : : "a" (k), "d" (n) \
+		       : : "a" (_prev), "a" (_next), "d" (_tssoff), \
+		           "d" (_shared) \
 		       : "d0", "d1", "d2", "d3", "d4", "d5", "a0", "a1"); \
 }
 
@@ -74,7 +81,7 @@
 
 #define iret() __asm__ __volatile__ ("rte": : :"memory", "sp", "cc")
 
-#if 1
+#ifndef CONFIG_RMW_INSNS
 static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
 {
   unsigned long tmp, flags;
diff -urN pc-2.0.21/include/asm-m68k/tiga.h linux-2.0.21/include/asm-m68k/tiga.h
--- pc-2.0.21/include/asm-m68k/tiga.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/include/asm-m68k/tiga.h	Sat Aug 24 14:45:31 1996
@@ -0,0 +1,74 @@
+#ifndef _TIGA_H
+#define _TIGA_H
+
+/*
+ * Host control register bits
+ */
+#define HLT	 0x8000      /* Halt GSP */
+#define CF	 0x4000      /* Cache Flush */
+#define LBL	 0x2000      /* Lower Byte Last */
+#define INCR	 0x1000      /* Increment on read cycles */
+#define INCW	 0x0800      /* Increment on write cycles */
+#define NMIMODE  0x0200      /* NMI mode (no stack usage) */
+#define NMI	 0x0100      /* Non Maskable Int */
+#define INTOUT	 0x0080      /* MSG Interrupt out */
+#define MSGOUT	 0x0070      /* Message out */
+#define INTIN	 0x0008      /* MSG Interrupt in */
+#define MSGIN	 0x0007      /* Message in */
+
+/*
+ * TIGA I/O registers.
+ */
+#define HESYNC    0xC0000000
+#define HEBLNK    0xC0000010
+#define HSBLNK    0xC0000020
+#define HTOTAL    0xC0000030
+#define VESYNC    0xC0000040
+#define VEBLNK    0xC0000050
+#define VSBLNK    0xC0000060
+#define VTOTAL    0xC0000070
+#define DPYCTL    0xC0000080
+#define DPYSTRT   0xC0000090
+#define DPYINT    0xC00000A0
+#define CONTROL   0xC00000B0
+#define HSTDATA   0xC00000C0
+#define HSTADRL   0xC00000D0
+#define HSTADRH   0xC00000E0
+#define HSTCTLL   0xC00000F0
+#define HSTCTLH   0xC0000100
+#define INTENB    0xC0000110
+#define INTPEND   0xC0000120
+#define CONVSP    0xC0000130
+#define CONVDP    0xC0000140
+#define PSIZE     0xC0000150
+#define PMASK     0xC0000160
+#define DPYTAP    0xC00001B0
+#define HCOUNT    0xC00001C0
+#define VCOUNT    0xC00001D0
+#define DPYADR    0xC00001E0
+#define REFCNT    0xC00001F0
+
+struct ioregs {
+	__volatile__ short hadrl;
+	__volatile__ short hadrh;
+	__volatile__ short data;
+	__volatile__ short ctrl;
+};
+
+/*
+ * Macros for access the host I/O-registers.
+ */
+#define gspsethadr(gsp, adr)			\
+     do{					\
+		gsp->hadrh = ((adr) >> 16);	\
+		gsp->hadrl = ((adr) & 0xffff);	\
+     }while(0)
+
+#define gspsethadrl(gsp, adr)			\
+     do{					\
+		gsp->hadrl = ((adr) & 0xffff);	\
+     } while (0)
+
+#define gspgethadr(gsp)	((gsp->hadrh) << 16 | ((gsp)->hadrl))
+
+#endif /* _TIGA_H */
diff -urN pc-2.0.21/include/asm-m68k/unistd.h linux-2.0.21/include/asm-m68k/unistd.h
--- pc-2.0.21/include/asm-m68k/unistd.h	Thu May 16 08:05:11 1996
+++ linux-2.0.21/include/asm-m68k/unistd.h	Sat Aug 24 14:45:33 1996
@@ -437,6 +437,10 @@
 {
 	register long retval __asm__ ("d0") = __NR_clone;
 	register long clone_arg __asm__ ("d1") = flags | CLONE_VM;
+	unsigned short fs;
+
+	fs = get_fs();
+	set_fs (KERNEL_DS);
 
 	__asm__ __volatile__
 	  ("movel %%sp,%%d2\n\t"
@@ -453,6 +457,7 @@
 	     "r" (arg), "a" (fn), "d" (clone_arg)
 	   : "d0", "d2");
 
+	set_fs (fs);
 	return retval;
 }
 
diff -urN pc-2.0.21/include/asm-m68k/zorro.h linux-2.0.21/include/asm-m68k/zorro.h
--- pc-2.0.21/include/asm-m68k/zorro.h	Mon May 20 06:54:29 1996
+++ linux-2.0.21/include/asm-m68k/zorro.h	Thu Sep 26 22:26:11 1996
@@ -21,18 +21,27 @@
  * Defined Board Manufacturers
  *
  * Please update arch/m68k/amiga/zorro.c if you make changes here
- * Many IDs were obtained by using ExpName V1.4 ((C) Richard Körber)
- * and by looking at the NetBSD-Amiga kernel source
+ * Many IDs were obtained from ExpName/Identify ((C) Richard Körber)
+ * and by looking at the NetBSD-Amiga kernel sources
  */
 
+#define MANUF_PACIFIC          (0x00D3)	/* Pacific Peripherals */
+#define PROD_SE_2000_A500      (0x00)	/* SE 2000 A500 */
+#define PROD_PACIFIC_HD        (0x0A)	/* HD Controller */
+
+#define MANUF_KUPKE            (0x00DD)	/* Kupke */
+#define PROD_GOLEM_BOX_2       (0x00)	/* Golem RAM Box 2MB */
+
 #define MANUF_MEMPHIS          (0x0100)	/* Memphis */
 #define PROD_STORMBRINGER      (0x00)	/* Stormbringer */
 
-#define MANUF_COMMODORE2       (0x0201)	/* Commodore Germany */
-#define PROD_A2088             (0x01)	/* CBM A2088 Bridgeboard */
+#define MANUF_COMMODORE2       (0x0201)	/* Commodore Braunschweig */
+#define PROD_A2088             (0x01)	/* CBM A2088 XT Bridgeboard */
+#define PROD_A2286             (0x02)	/* CBM A2286 AT Bridgeboard */
+#define PROD_A4091_2           (0x54)	/* CBM A4091 SCSI Controller */
 #define PROD_A2386SX           (0x67)	/* CBM A2386-SX Bridgeboard */
 
-#define MANUF_COMMODORE        (0x0202)	/* Commodore USA */
+#define MANUF_COMMODORE        (0x0202)	/* Commodore West Chester */
 #define PROD_A2090A            (0x01)	/* CBM A2090/A2090A HD Controller */
 #define PROD_A590              (0x02)	/* CBM A590 SCSI Controller */
 #define PROD_A2091             (0x03)	/* CBM A2091 SCSI Controller */
@@ -45,19 +54,57 @@
 #define PROD_A2620             (0x50)	/* CBM A2620 68020/RAM Card */
 #define PROD_A2630             (0x51)	/* CBM A2630 68030/RAM Card */
 #define PROD_A4091             (0x54)	/* CBM A4091 SCSI Controller */
+#define PROD_A2065_2           (0x5A)	/* A2065 Ethernet Card */
 #define PROD_ROMULATOR         (0x60)	/* CBM Romulator Card */
 #define PROD_A3000TESTFIX      (0x61)	/* CBM A3000 Test Fixture */
+#define PROD_A2386SX_2         (0x67)	/* A2386-SX Bridgeboard */
 #define PROD_A2065             (0x70)	/* CBM A2065 Ethernet Card */
 
+#define MANUF_COMMODORE3       (0x0203)	/* Commodore West Chester */
+#define PROD_A2090A_CM         (0x03)	/* A2090A Combitec/MacroSystem */
+
+#define MANUF_KCS              (0x02FF)	/* Kolff Computer Supplies */
+#define PROD_POWER_BOARD       (0x00)	/* KCS Power PC Board */
+
 #define MANUF_CARDCO           (0x03EC)	/* Cardco */
+#define PROD_KRONOS_2000_SCSI  (0x04)	/* Kronos 2000 SCSI Controller */
+#define PROD_A1000_SCSI        (0x0C)	/* A1000 SCSI Controller */
+#define PROD_ESCORT_SCSI       (0x0E)	/* Escort SCSI Controller */
 #define PROD_CC_A2410          (0xF5)	/* Cardco A2410 Hires Graphics Card */
 
+#define MANUF_A_SQUARED        (0x03ED)	/* A-Squared */
+#define PROD_LIVE_2000         (0x01)	/* Live! 2000 */
+
+#define MANUF_COMSPEC          (0x03EE)	/* ComSpec Communications */
+#define PROD_AX2000            (0x01)	/* AX2000 */
+
+#define MANUF_ANAKIN           (0x03F1)	/* Anakin */
+#define PROD_EASYL             (0x01)	/* Easyl Tablet */
+
 #define MANUF_MICROBOTICS      (0x03F2)	/* MicroBotics */
+#define PROD_STARBOARD_II      (0x00)	/* StarBoard II */
+#define PROD_STARDRIVE         (0x02)	/* StarDrive */
+#define PROD_8_UP_A            (0x03)	/* 8-Up (Rev A) */
+#define PROD_8_UP_Z            (0x04)	/* 8-Up (Rev Z) */
+#define PROD_VXL_RAM           (0x44)	/* VXL RAM */
 #define PROD_VXL_30            (0x45)	/* VXL-30 Turbo Board */
+#define PROD_MBX_1200          (0x81)	/* MBX 1200 */
+#define PROD_HARDFRAME_2000    (0x9E)	/* Hardframe 2000 */
+#define PROD_MBX_1200_2        (0xC1)	/* MBX 1200 */
+
+#define MANUF_ACCESS           (0x03F4)	/* Access Associates */
+
+#define MANUF_EXPANSION_TECH   (0x03F6)	/* Expansion Technologies */
 
 #define MANUF_ASDG             (0x03FF)	/* ASDG */
+#define PROD_ASDG_MEMORY       (0x01)	/* Memory Expansion */
+#define PROD_ASDG_MEMORY_2     (0x02)	/* Memory Expansion */
 #define PROD_LAN_ROVER         (0xFE)	/* Lan Rover Ethernet */
-#define PROD_ASDG_DUAL_SERIAL  (0xFF)	/* Dual Serial Card */
+#define PROD_TWIN_X            (0xFF)	/* Twin-X Serial Card */
+
+#define MANUF_IMTRONICS        (0x0404)	/* Imtronics */
+#define PROD_HURRICANE_2800    (0x39)	/* Hurricane 2800 68030 */
+#define PROD_HURRICANE_2800_2  (0x57)	/* Hurricane 2800 68030 */
 
 #define MANUF_UNIV_OF_LOWELL   (0x0406)	/* University of Lowell */
 #define PROD_A2410             (0x00)	/* CBM A2410 Hires Graphics Card */
@@ -68,20 +115,38 @@
 #define PROD_A4066             (0x0A)	/* A4066 Ethernet Card */
 
 #define MANUF_SUPRA            (0x0420)	/* Supra */
+#define PROD_SUPRADRIVE_4x4    (0x01)	/* SupraDrive 4x4 SCSI Controller */
+#define PROD_SUPRA_2000        (0x03)	/* 2000 DMA HD */
+#define PROD_SUPRA_500         (0x05)	/* 500 HD/RAM */
+#define PROD_SUPRA_500RX       (0x09)	/* 500RX/2000 RAM */
+#define PROD_SUPRA_500RX_2     (0x0A)	/* 500RX/2000 RAM */
+#define PROD_SUPRA_2400ZI      (0x0B)	/* 2400zi Modem */
 #define PROD_WORDSYNC          (0x0C)	/* Supra Wordsync SCSI Controller */
 #define PROD_WORDSYNC_II       (0x0D)	/* Supra Wordsync II SCSI Controller */
-#define PROD_SUPRA_2400MODEM   (0x10)	/* Supra 2400 Modem */
+#define PROD_SUPRA_2400ZIPLUS  (0x10)	/* 2400zi+ Modem */
 
 #define MANUF_CSA              (0x0422)	/* CSA */
 #define PROD_MAGNUM            (0x11)	/* Magnum 40 SCSI Controller */
 #define PROD_12GAUGE           (0x15)	/* 12 Gauge SCSI Controller */
 
+#define MANUF_GVP3             (0x06E1)	/* Great Valley Products */
+#define PROD_IMPACT            (0x08)	/* Impact SCSI/Memory */
+
+#define MANUF_BYTEBOX          (0x07DA)	/* ByteBox */
+#define PROD_BYTEBOX_A500      (0x00)	/* A500 */
+
 #define MANUF_HACKER           (0x07DB)	/* Test only: no product definitions */
 
-#define MANUF_POWER_COMPUTING  (0x07DC)	/* Power Computing */
-#define PROD_DKB_1240          (0x12)	/* Viper II Turbo Board (DKB 1240) */
+#define MANUF_POWER_COMPUTING  (0x07DC)	/* Power Computing (DKB) */
+#define PROD_DKB_3128          (0x0E)	/* DKB 3128 RAM */
+#define PROD_VIPER_II_COBRA    (0x12)	/* Viper II Turbo Board (DKB Cobra) */
 
 #define MANUF_GVP              (0x07E1)	/* Great Valley Products */
+#define PROD_IMPACT_I_4K       (0x01)	/* Impact Series-I SCSI 4K */
+#define PROD_IMPACT_I_16K_2    (0x02)	/* Impact Series-I SCSI 16K/2 */
+#define PROD_IMPACT_I_16K_3    (0x03)	/* Impact Series-I SCSI 16K/3 */
+#define PROD_IMPACT_3001_IDE   (0x08)	/* Impact 3001 IDE */
+#define PROD_IMPACT_3001_RAM   (0x09)	/* Impact 3001 RAM */
 #define PROD_GVPIISCSI         (0x0B)	/* GVP Series II SCSI Controller */
 #define PROD_GVPIISCSI_2       (0x09)	/* evidence that the driver works
 					   for this product code also */
@@ -90,10 +155,18 @@
 					   GVP products - use the epc to
 					   identify it correctly */
 #define PROD_GVP_A2000_030     (0x0D)	/* GVP A2000 68030 Turbo Board */
+#define PROD_IMPACT_3001_IDE_2 (0x0D)	/* Impact 3001 IDE */
 #define PROD_GFORCE_040_SCSI   (0x16)	/* GForce 040 with SCSI (new) */
 #define PROD_GVPIV_24          (0x20)	/* GVP IV-24 Graphics Board */
+#define PROD_GFORCE_040        (0xFF)	/* GForce 040 Turbo Board */
 /* #define PROD_GVPIO_EXT      (0xFF)*/	/* GVP I/O Extender */
 
+#define MANUF_SYNERGY          (0x07E5)	/* Synergy */
+
+#define MANUF_XETEC            (0x07E6)	/* Xetec */
+#define PROD_FASTCARD_SCSI     (0x01)	/* FastCard SCSI Controller */
+#define PROD_FASTCARD_RAM      (0x02)	/* FastCard RAM */
+
 #define MANUF_PPI              (0x07EA)	/* Progressive Peripherals Inc. */
 #define PROD_MERCURY           (0x00)	/* Mercury Turbo Board */
 #define PROD_PPS_A3000_040     (0x01)	/* PP&S A3000 68040 Turbo Board */
@@ -101,11 +174,22 @@
 #define PROD_ZEUS              (0x96)	/* Zeus SCSI Controller */
 #define PROD_PPS_A500_040      (0xBB)	/* PP&S A500 68040 Turbo Board */
 
+#define MANUF_XEBEC            (0x07EC)	/* Xebec */
+
+#define MANUF_SPIRIT           (0x07F2)	/* Spirit */
+#define PROD_HDA_506           (0x04)	/* HDA 506 Harddisk */
+#define PROD_OCTABYTE_RAM      (0x06)	/* OctaByte RAM */
+
 #define MANUF_BSC              (0x07FE)	/* BSC */
 #define PROD_ALF_3_SCSI        (0x03)	/* BSC ALF 3 SCSI Controller */
 
+#define MANUF_BSC3             (0x0801)	/* BSC */
+#define PROD_ALF_2_SCSI        (0x01)	/* ALF 2 SCSI Controller */
+#define PROD_ALF_3_SCSI_2      (0x03)	/* ALF 3 SCSI Controller */
+
 #define MANUF_C_LTD            (0x0802)	/* C Ltd. */
 #define PROD_KRONOS_SCSI       (0x04)	/* Kronos SCSI Controller */
+#define PROD_A1000_SCSI_2      (0x0C)	/* A1000 SCSI Controller */
 
 #define MANUF_JOCHHEIM         (0x0804)	/* Jochheim */
 #define PROD_JOCHHEIM_RAM      (0x01)	/* Jochheim RAM */
@@ -113,42 +197,71 @@
 #define MANUF_CHECKPOINT       (0x0807)	/* Checkpoint Technologies */
 #define PROD_SERIAL_SOLUTION   (0x00)	/* Serial Solution */
 
-#define MANUF_GOLEM            (0x0819)	/* Golem */
-#define PROD_GOLEM_SCSI_II     (0x02)	/* Golem SCSI-II Controller */
+#define MANUF_ICD              (0x0817)	/* ICD */
+#define PROD_ADVANTAGE_2000    (0x01)	/* Advantage 2000 SCSI Controller */
 
-#define MANUF_HARDITAL_SYNTHES (0x0817)	/* Hardital Synthesis */
-#define PROD_HARDITAL_SCSI     (0x01)	/* Hardital Synthesis SCSI Controller */
+#define MANUF_KUPKE2           (0x0819)	/* Kupke */
+#define PROD_KUPKE_SCSI_II     (0x02)	/* Golem SCSI-II Controller */
+#define PROD_GOLEM_BOX         (0x03)	/* Golem Box */
+#define PROD_KUPKE_SCSI_AT     (0x05)	/* SCSI/AT Controller */
 
-#define MANUF_HARDITAL2        (0x0820)	/* Hardital Synthesis */
+#define MANUF_HARDITAL         (0x0820)	/* Hardital Synthesis */
 #define PROD_TQM               (0x14)	/* TQM 68030+68882 Turbo Board */
 
 #define MANUF_BSC2             (0x082C)	/* BSC */
 #define PROD_OKTAGON_SCSI      (0x05)	/* BSC Oktagon 2008 SCSI Controller */
-#define PROD_TANDEM            (0x06)	/* BSC Tandem */
+#define PROD_TANDEM            (0x06)	/* BSC Tandem AT-2008/508 IDE */
 #define PROD_OKTAGON_RAM       (0x08)	/* BSC Oktagon 2008 RAM */
 #define PROD_MULTIFACE_I       (0x10)	/* Alfa Data MultiFace I */
 #define PROD_MULTIFACE_II      (0x11)	/* Alfa Data MultiFace II */
 #define PROD_MULTIFACE_III     (0x12)	/* Alfa Data MultiFace III */
-#define PROD_ISDN_MASTER       (0x40)	/* BSC ISDN Master */
+#define PROD_ISDN_MASTERCARD   (0x40)	/* BSC ISDN MasterCard */
+#define PROD_ISDN_MASTERCARD_2 (0x41)	/* BSC ISDN MasterCard II */
 
 #define MANUF_ADV_SYS_SOFT     (0x0836)	/* Advanced Systems & Software */
 #define PROD_NEXUS_SCSI        (0x01)	/* Nexus SCSI Controller */
 #define PROD_NEXUS_RAM         (0x08)	/* Nexus RAM */
 
+#define MANUF_IMPULSE          (0x0838)	/* Impulse */
+#define PROD_FIRECRACKER_24    (0x00)	/* FireCracker 24 */
+
 #define MANUF_IVS              (0x0840)	/* IVS */
-#define PROD_TRUMPCARD_500     (0x30)	/* Trumpcard 500 SCSI Controller */
-#define PROD_TRUMPCARD         (0x34)	/* Trumpcard SCSI Controller */
+#define PROD_GRANDSLAM         (0x04)	/* GrandSlam RAM */
+#define PROD_IVS_OVERDRIVE     (0x10)	/* OverDrive HD */
+#define PROD_TRUMPCARD_CLASSIC (0x30)	/* Trumpcard Classic SCSI Controller */
+#define PROD_TRUMPCARD_PRO     (0x34)	/* Trumpcard Pro SCSI Controller */
+#define PROD_META_4            (0x40)	/* Meta-4 RAM */
 #define PROD_VECTOR            (0xF3)	/* Vector SCSI Controller */
 
+#define MANUF_VECTOR           (0x0841)	/* Vector */
+#define PROD_CONNECTION        (0xE3)	/* Connection Serial IO */
+
 #define MANUF_XPERT_PRODEV     (0x0845)	/* XPert/ProDev */
+#define PROD_VISIONA_RAM       (0x01)	/* Visiona Graphics Board */
+#define PROD_VISIONA_REG       (0x02)
 #define PROD_MERLIN_RAM        (0x03)	/* Merlin Graphics Board */
 #define PROD_MERLIN_REG        (0x04)
 
 #define MANUF_HYDRA_SYSTEMS    (0x0849)	/* Hydra Systems */
 #define PROD_AMIGANET          (0x01)	/* Amiganet Board */
 
-#define MANUF_DIG_MICRONICS    (0x0851)	/* Digital Micronics Inc */
+#define MANUF_SUNRIZE          (0x084F)	/* Sunrize Industries */
+#define PROD_AD516             (0x02)	/* AD516 Audio */
+
+#define MANUF_TRICERATOPS      (0x0850)	/* Triceratops */
+#define PROD_TRICERATOPS       (0x01)	/* Triceratops Multi I/O Board */
+
+#define MANUF_APPLIED_MAGIC    (0x0851)	/* Applied Magic Inc */
 #define PROD_DMI_RESOLVER      (0x01)	/* DMI Resolver Graphics Board */
+#define PROD_DIGITAL_BCASTER   (0x06)	/* Digital Broadcaster */
+
+#define MANUF_GFX_BASE         (0x085E)	/* GFX-Base */
+#define PROD_GDA_1_RAM         (0x00)	/* GDA-1 Graphics Board */
+#define PROD_GDA_1_REG         (0x01)
+
+#define MANUF_ROCTEC           (0x0860)	/* RocTec */
+#define PROD_RH_800C           (0x01)	/* RH 800C Hard Disk Controller */
+#define PROD_RH_800C_RAM       (0x01)	/* RH 800C RAM */
 
 #define MANUF_HELFRICH1        (0x0861)	/* Helfrich */
 #define PROD_RAINBOW3          (0x21)	/* Rainbow3 Graphics Board */
@@ -156,52 +269,101 @@
 #define MANUF_SW_RESULT_ENTS   (0x0866)	/* Software Result Enterprises */
 #define PROD_GG2PLUS           (0x01)	/* GG2+ Bus Converter */
 
+#define MANUF_MASOBOSHI        (0x086D)	/* Masoboshi */
+#define PROD_MASTER_CARD_RAM   (0x03)	/* Master Card RAM */
+#define PROD_MASTER_CARD_SCSI  (0x04)	/* Master Card SCSI Controller */
+#define PROD_MVD_819           (0x07)	/* MVD 819 */
+
+#define MANUF_DELACOMP         (0x0873)	/* DelaComp */
+#define PROD_DELACOMP_RAM_2000 (0x01)	/* RAM Expansion 2000 */
+
 #define MANUF_VILLAGE_TRONIC   (0x0877)	/* Village Tronic */
+#define PROD_DOMINO_RAM        (0x01)	/* Domino Graphics Board (RAM) */
+#define PROD_DOMINO_REG        (0x02)	/* Domino Graphics Board (REG) */
 #define PROD_PICASSO_II_RAM    (0x0B)	/* Picasso II Graphics Board */
 #define PROD_PICASSO_II_REG    (0x0C)
+#define PROD_PICASSO_II_REG_2  (0x0D)
 #define PROD_ARIADNE           (0xC9)	/* Ariadne Ethernet */
 
 #define MANUF_UTILITIES_ULTD   (0x087B)	/* Utilities Unlimited */
 #define PROD_EMPLANT_DELUXE    (0x15)	/* Emplant Deluxe SCSI Controller */
 #define PROD_EMPLANT_DELUXE2   (0x20)	/* Emplant Deluxe SCSI Controller */
 
+#define MANUF_AMITRIX          (0x0880)	/* Amitrix */
+#define PROD_AMITRIX_MULTI_IO  (0x01)	/* Multi-IO */
+#define PROD_AMITRIX_CD_RAM    (0x02)	/* CD-RAM Memory */
+
 #define MANUF_MTEC             (0x0890)	/* MTEC Germany */
 #define PROD_MTEC_68030        (0x03)	/* 68030 Turbo Board */
-#define PROD_MTEC_T1230        (0x20)	/* MTEC T1230/28 Turbo Board */
+#define PROD_MTEC_T1230        (0x20)	/* A1200 T68030/42 RTC Turbo Board */
+#define PROD_MTEC_RAM          (0x22)	/* MTEC 8MB RAM */
 
 #define MANUF_GVP2             (0x0891)	/* Great Valley Products */
-#define PROD_SPECTRUM_RAM      (0x01)	/* GVP Spectrum Graphics Board */
+#define PROD_SPECTRUM_RAM      (0x01)	/* EGS 28/24 Spectrum Graphics Board */
 #define PROD_SPECTRUM_REG      (0x02)
 
 #define MANUF_HELFRICH2        (0x0893)	/* Helfrich */
 #define PROD_PICCOLO_RAM       (0x05)	/* Piccolo Graphics Board */
 #define PROD_PICCOLO_REG       (0x06)
 #define PROD_PEGGY_PLUS        (0x07)	/* PeggyPlus MPEG Decoder Board */
+#define PROD_VIDEOCRUNCHER     (0x08)	/* VideoCruncher */
 #define PROD_SD64_RAM          (0x0A)	/* SD64 Graphics Board */
 #define PROD_SD64_REG          (0x0B)
 
 #define MANUF_MACROSYSTEMS     (0x089B)	/* MacroSystems USA */
-#define PROD_WARP_ENGINE       (0x13)	/* Warp Engine SCSI Controller */
+#define PROD_WARP_ENGINE       (0x13)	/* Warp Engine 40xx SCSI Controller */
+
+#define MANUF_ELBOX            (0x089E)	/* ElBox Computer */
+#define PROD_ELBOX_1200        (0x06)	/* Elbox 1200/4 RAM */
 
 #define MANUF_HARMS_PROF       (0x0A00)	/* Harms Professional */
+#define PROD_HARMS_030_PLUS    (0x10)	/* 030 plus */
 #define PROD_3500_TURBO        (0xD0)	/* 3500 Turbo board */
 
+#define MANUF_MICRONIK         (0x0A50)	/* Micronik */
+#define PROD_RCA_120           (0x0A)	/* RCA 120 RAM */
+
+#define MANUF_IMTRONICS2       (0x1028)	/* Imtronics */
+#define PROD_HURRICANE_2800_3  (0x39)	/* Hurricane 2800 68030 */
+#define PROD_HURRICANE_2800_4  (0x57)	/* Hurricane 2800 68030 */
+
+#define MANUF_KUPKE3           (0x1248)	/* Kupke */
+#define PROD_GOLEM_3000        (0x01)	/* Golem HD 3000 */
+
+#define MANUF_INFORMATION      (0x157C)	/* Information */
+#define PROD_ISDN_ENGINE_I     (0x64)	/* ISDN Engine I */
+
 #define MANUF_VORTEX           (0x2017)	/* Vortex */
-#define PROD_GOLDEN_GATE_386   (0x07)	/* Golden Gate 80386 Board */
+#define PROD_GOLDEN_GATE_386SX (0x07)	/* Golden Gate 80386SX Board */
 #define PROD_GOLDEN_GATE_RAM   (0x08)	/* Golden Gate RAM */
 #define PROD_GOLDEN_GATE_486   (0x09)	/* Golden Gate 80486 Board */
 
 #define MANUF_DATAFLYER        (0x2062)	/* DataFlyer */
-#define PROD_DATAFLYER_4000SX  (0x01)	/* DataFlyer 4000SX SCSI Controller */
+#define PROD_DATAFLYER_4000SXS (0x01)	/* DataFlyer 4000SX SCSI Controller */
+#define PROD_DATAFLYER_4000SXR (0x02)	/* DataFlyer 4000SX RAM */
+
+#define MANUF_READYSOFT        (0x2100)	/* ReadySoft */
+#define PROD_AMAX              (0x01)	/* AMax II/IV */
 
 #define MANUF_PHASE5           (0x2140)	/* Phase5 */
+#define PROD_BLIZZARD_RAM      (0x01)	/* Blizzard RAM */
+#define PROD_BLIZZARD          (0x02)	/* Blizzard */
+#define PROD_BLIZZARD_1220_IV  (0x06)	/* Blizzard 1220-IV Turbo Board */
 #define PROD_FASTLANE_RAM      (0x0A)	/* FastLane RAM */
-#define PROD_FASTLANE_SCSI     (0x0B)	/* FastLane/Blizzard 1230-II SCSI */
-#define PROD_CYBERSTORM_SCSI   (0x0C)	/* CyberStorm Fast SCSI-II Controller */
+#define PROD_FASTLANE_SCSI     (0x0B)	/* FastLane/Blizzard 1230-II SCSI/CyberSCSI */
+#define PROD_CYBERSTORM_SCSI   (0x0C)	/* Blizzard 1220/CyberStorm */
 #define PROD_BLIZZARD_1230_III (0x0D)	/* Blizzard 1230-III Turbo Board */
-#define PROD_BLIZZARD_1230_IV  (0x11)	/* Blizzard 1230-IV Turbo Board */
+#define PROD_BLIZZARD_1230_IV  (0x11)	/* Blizzard 1230-IV/1260 Turbo Board */
+#define PROD_BLIZZARD_2060SCSI (0x18)	/* Blizzard 2060 SCSI Controller */
+#define PROD_CYBERSTORM        (0x19)	/* CyberStorm */
 #define PROD_CYBERVISION       (0x22)	/* CyberVision64 Graphics Board */
 
+#define MANUF_DPS              (0x2169)	/* DPS */
+#define PROD_DPS_PAR           (0x01)	/* Personal Animation Recorder */
+
+#define MANUF_APOLLO2          (0x2200)	/* Apollo */
+#define PROD_A620              (0x00)	/* A620 68020 Accelerator */
+
 #define MANUF_APOLLO           (0x2222)	/* Apollo */
 #define PROD_AT_APOLLO         (0x22)	/* AT-Apollo */
 #define PROD_APOLLO_TURBO      (0x23)	/* Apollo Turbo Board */
@@ -215,11 +377,24 @@
 #define PROD_MAESTRO_PRO       (0x05)	/* Maestro Pro */
 #define PROD_RETINA_Z2         (0x06)	/* Retina Z2 Graphics Board */
 #define PROD_MULTI_EVOLUTION   (0x08)	/* MultiEvolution */
+#define PROD_TOCCATA           (0x0C)	/* Toccata Sound Board */
 #define PROD_RETINA_Z3         (0x10)	/* Retina Z3 Graphics Board */
+#define PROD_VLAB_MOTION       (0x12)	/* VLab Motion */
 #define PROD_FALCON_040        (0xFD)	/* Falcon '040 Turbo Board */
 
+#define MANUF_COMBITEC         (0x6766)	/* Combitec */
+
+#define MANUF_SKI              (0x8000)	/* SKI Peripherals */
+#define PROD_SKI_SCSI_SERIAL   (0x80)	/* SCSI / Dual Serial */
 
-/* Illegal Manufacturer IDs. These do NOT appear in amiga/zorro.c! */
+#define MANUF_CAMERON          (0xAA01)	/* Cameron */
+#define PROD_CAMERON_SCANNER   (0x10)	/* Scanner Interface */
+
+#define MANUF_REIS_WARE        (0xAA11)	/* Reis-Ware */
+#define PROD_RW_HANDYSCANNER   (0x11)	/* Handyscanner */
+
+
+/* Illegal Manufacturer IDs. These do NOT appear in arch/m68k/amiga/zorro.c! */
 
 #define MANUF_HACKER_INC       (0x07DB)	/* Hacker Inc. */
 #define PROD_HACKER_SCSI       (0x01)	/* Hacker Inc. SCSI Controller */
@@ -227,14 +402,21 @@
 #define MANUF_RES_MNGT_FORCE   (0x07DB)	/* Resource Management Force */
 #define PROD_QUICKNET          (0x02)	/* QuickNet Ethernet */
 
+#define MANUF_VECTOR2          (0x07DB)	/* Vector */
+#define PROD_CONNECTION_2      (0xE0)	/* Vector Connection */
+#define PROD_CONNECTION_3      (0xE1)	/* Vector Connection */
+#define PROD_CONNECTION_4      (0xE2)	/* Vector Connection */
+#define PROD_CONNECTION_5      (0xE3)	/* Vector Connection */
+
 
 /*
  * GVP's identifies most of their product through the 'extended
- * product code' (epc). The epc has to be and'ed with the GVPEPCMASK
+ * product code' (epc). The epc has to be and'ed with the GVP_PRODMASK
  * before the identification.
  */
 
-#define GVP_EPCMASK (0xf8)
+#define GVP_PRODMASK    (0xf8)
+#define GVP_SCSICLKMASK (0x01)
 
 enum GVP_ident {
   GVP_GFORCE_040      = 0x20,
diff -urN pc-2.0.21/include/asm-mips/bootinfo.h linux-2.0.21/include/asm-mips/bootinfo.h
--- pc-2.0.21/include/asm-mips/bootinfo.h	Thu Dec 14 07:16:53 1995
+++ linux-2.0.21/include/asm-mips/bootinfo.h	Sat Aug 24 14:45:37 1996
@@ -8,7 +8,7 @@
  * Copyright (C) 1992 by Greg Harp
  *
  * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file README.legal in the main directory of this archive
+ * License.  See the file COPYING in the main directory of this archive
  * for more details.
  */
 #ifndef __ASM_MIPS_BOOTINFO_H
diff -urN pc-2.0.21/include/asm-mips/setup.h linux-2.0.21/include/asm-mips/setup.h
--- pc-2.0.21/include/asm-mips/setup.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/include/asm-mips/setup.h	Sat Aug 24 14:45:54 1996
@@ -0,0 +1,4 @@
+#ifndef _MIPS_SETUP_H
+#define _MIPS_SETUP_H
+
+#endif /* _MIPS_SETUP_H */
diff -urN pc-2.0.21/include/asm-ppc/setup.h linux-2.0.21/include/asm-ppc/setup.h
--- pc-2.0.21/include/asm-ppc/setup.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/include/asm-ppc/setup.h	Sat Aug 24 14:46:20 1996
@@ -0,0 +1,4 @@
+#ifndef _PPC_SETUP_H
+#define _PPC_SETUP_H
+
+#endif /* _PPC_SETUP_H */
diff -urN pc-2.0.21/include/asm-sparc/setup.h linux-2.0.21/include/asm-sparc/setup.h
--- pc-2.0.21/include/asm-sparc/setup.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/include/asm-sparc/setup.h	Sat Aug 24 14:47:02 1996
@@ -0,0 +1,4 @@
+#ifndef _SPARC_SETUP_H
+#define _SPARC_SETUP_H
+
+#endif /* _SPARC_SETUP_H */
diff -urN pc-2.0.21/include/linux/affs_fs_i.h linux-2.0.21/include/linux/affs_fs_i.h
--- pc-2.0.21/include/linux/affs_fs_i.h	Sun May 19 14:22:20 1996
+++ linux-2.0.21/include/linux/affs_fs_i.h	Wed Sep 18 19:44:45 1996
@@ -1,24 +1,45 @@
 #ifndef _AFFS_FS_I
 #define _AFFS_FS_I
 
-#define EXT_CACHE_SIZE	12
-#define MAX_PREALLOC	8	/* MUST be a power of 2 */
+#include <linux/a.out.h>
+#include <linux/time.h>
+
+#define AFFS_MAX_PREALLOC	16	/* MUST be a power of 2 */
+#define AFFS_KCSIZE		73	/* Allows for 1 extension block at 512 byte-blocks */
+
+struct key_cache {
+	struct timeval	 kc_lru_time;	/* Last time this cache was used */
+	int	 kc_first;		/* First cached key */
+	int	 kc_last;		/* Last cached key */
+	int	 kc_this_key;		/* Key of extension block this data block keys are from */
+	int	 kc_this_seq;		/* Sequence number of this extension block */
+	int	 kc_next_key;		/* Key of next extension block */
+	int	 kc_keys[AFFS_KCSIZE];	/* Key cache */
+};
+
+#define EC_SIZE	(PAGE_SIZE - 4 * sizeof(struct key_cache) - 4) / 4
+
+struct ext_cache {
+	struct key_cache  kc[4];	/* The 4 key caches */
+	__s32	 ec[EC_SIZE];		/* Keys of assorted extension blocks */
+	int	 max_ext;		/* Index of last known extension block */
+};
 
 /*
  * affs fs inode data in memory
  */
 struct affs_inode_info {
-	__u32	i_protect;		/* unused attribute bits */
-	__s32	i_parent;		/* parent ino */
-	__s32	i_original;		/* if != 0, this is the key of the original */
-	__s32	i_ext[EXT_CACHE_SIZE];	/* extension block numbers */
-	__s32	i_data[MAX_PREALLOC];	/* preallocated blocks */
-	int	i_lastblock;		/* last allocated block */
-	short	i_max_ext;		/* last known extension block */
-	short	i_pa_cnt;		/* number of preallocated blocks */
-	short	i_pa_next;		/* Index of next block in i_data[] */
-	short	i_pa_last;		/* Index of next free slot in i_data[] */
-	short	i_zone;			/* write zone */
+	__u32	 i_protect;		/* unused attribute bits */
+	__s32	 i_parent;		/* parent ino */
+	__s32	 i_original;		/* if != 0, this is the key of the original */
+	__s32	 i_data[AFFS_MAX_PREALLOC];	/* preallocated blocks */
+	struct ext_cache *i_ec;		/* Cache gets allocated dynamically */
+	int	 i_cache_users;		/* Cache cannot be freed while > 0 */
+	int	 i_lastblock;		/* last allocated block */
+	short	 i_pa_cnt;		/* number of preallocated blocks */
+	short	 i_pa_next;		/* Index of next block in i_data[] */
+	short	 i_pa_last;		/* Index of next free slot in i_data[] */
+	short	 i_zone;		/* write zone */
 	unsigned char i_hlink;		/* This is a fake */
 	unsigned char i_pad;
 };
diff -urN pc-2.0.21/include/linux/blk.h linux-2.0.21/include/linux/blk.h
--- pc-2.0.21/include/linux/blk.h	Fri Sep 20 15:23:18 1996
+++ linux-2.0.21/include/linux/blk.h	Thu Sep 26 22:25:16 1996
@@ -40,6 +40,9 @@
 #ifdef CONFIG_CDU31A
 extern int cdu31a_init(void);
 #endif CONFIG_CDU31A
+#ifdef CONFIG_ATARI_ACSI
+extern int acsi_init(void);
+#endif CONFIG_ATARI_ACSI
 #ifdef CONFIG_MCD
 extern int mcd_init(void);
 #endif CONFIG_MCD
@@ -85,6 +88,9 @@
 #ifdef CONFIG_BLK_DEV_MD
 extern int md_init(void);
 #endif CONFIG_BLK_DEV_MD
+#ifdef CONFIG_AMIGA_Z2RAM
+extern int z2_init(void);
+#endif
 
 extern void set_device_ro(kdev_t dev,int flag);
 void add_blkdev_randomness(int major);
@@ -135,6 +141,15 @@
 #define DEVICE_OFF(device)
 #define DEVICE_NO_RANDOM
 
+#elif (MAJOR_NR == Z2RAM_MAJOR)
+
+/* Zorro II Ram */
+#define DEVICE_NAME "Z2RAM"
+#define DEVICE_REQUEST do_z2_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
 #elif (MAJOR_NR == FLOPPY_MAJOR)
 
 static void floppy_off(unsigned int nr);
@@ -207,6 +222,15 @@
 #define DEVICE_NAME "CDU31A"
 #define DEVICE_REQUEST do_cdu31a_request
 #define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == ACSI_MAJOR) && defined (CONFIG_ATARI_ACSI)
+
+#define DEVICE_NAME "ACSI"
+#define DEVICE_INTR do_acsi
+#define DEVICE_REQUEST do_acsi_request
+#define DEVICE_NR(device) (MINOR(device) >> 4)
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
diff -urN pc-2.0.21/include/linux/elf.h linux-2.0.21/include/linux/elf.h
--- pc-2.0.21/include/linux/elf.h	Wed Aug  7 09:08:46 1996
+++ linux-2.0.21/include/linux/elf.h	Fri Sep 27 00:26:12 1996
@@ -152,6 +152,30 @@
 #define R_386_GOTPC	10
 #define R_386_NUM	11
 
+#define R_68K_NONE	0
+#define R_68K_32	1
+#define R_68K_16	2
+#define R_68K_8		3
+#define R_68K_PC32	4
+#define R_68K_PC16	5
+#define R_68K_PC8	6
+#define R_68K_GOT32	7
+#define R_68K_GOT16	8
+#define R_68K_GOT8	9
+#define R_68K_GOT32O	10
+#define R_68K_GOT16O	11
+#define R_68K_GOT8O	12
+#define R_68K_PLT32	13
+#define R_68K_PLT16	14
+#define R_68K_PLT8	15
+#define R_68K_PLT32O	16
+#define R_68K_PLT16O	17
+#define R_68K_PLT8O	18
+#define R_68K_COPY	19
+#define R_68K_GLOB_DAT	20
+#define R_68K_JMP_SLOT	21
+#define R_68K_RELATIVE	22
+
 typedef struct elf32_rel {
   Elf32_Addr	r_offset;
   Elf32_Word	r_info;
@@ -371,7 +395,12 @@
   unsigned int	n_type;		/* Content type */
 } Elf64_Nhdr;
 
+#ifdef __mc68000__
+#define ELF_START_MMAP 0xC0000000
+#endif
+#ifdef __i386__
 #define ELF_START_MMAP 0x80000000
+#endif
 
 #if ELF_CLASS == ELFCLASS32
 
diff -urN pc-2.0.21/include/linux/ext2_fs.h linux-2.0.21/include/linux/ext2_fs.h
--- pc-2.0.21/include/linux/ext2_fs.h	Sun Aug  4 13:38:04 1996
+++ linux-2.0.21/include/linux/ext2_fs.h	Sun Sep  8 17:12:42 1996
@@ -451,7 +451,7 @@
 /* dir.c */
 extern int ext2_check_dir_entry (const char *, struct inode *,
 				 struct ext2_dir_entry *, struct buffer_head *,
-				 unsigned long);
+				 unsigned long, int);
 
 /* file.c */
 extern int ext2_read (struct inode *, struct file *, char *, int);
@@ -527,6 +527,38 @@
 
 /* symlink.c */
 extern struct inode_operations ext2_symlink_inode_operations;
+
+/* byte swapping */
+#define e_swab(__doit, __val) \
+  ((__doit) ? (sizeof (__val) == 1 ? (__val) : \
+	       sizeof (__val) == 2 ? (__typeof (__val)) ext2_swab16 (__val) \
+				   : (__typeof (__val)) ext2_swab32 (__val)) \
+	    : (__val))
+#define e_set_swab(__doit, __var, __val) \
+  ((__var) = (__doit) ? (sizeof (__var) == 1 ? (__val) : \
+			 sizeof (__var) == 2 ? ext2_swab16 (__val) \
+					     : ext2_swab32 (__val)) \
+		      : (__val))
+
+extern __inline__ __u16 ext2_swab16 (__u16 val)
+{
+	return (val << 8) | (val >> 8);
+}
+
+extern __inline__ __u32 ext2_swab32 (__u32 val)
+{
+#ifdef __mc68000__
+	__asm__ ("rolw #8,%0; swap %0; rolw #8,%0" : "=d" (val) : "0" (val));
+	return val;
+#else
+	__u16 vall = val, valh = val >> 16;
+	vall = (vall << 8) | (vall >> 8);
+	valh = (valh << 8) | (valh >> 8);
+	return (vall << 16) | valh;
+#endif
+}
+
+#define BYTE_SWAP(A) A
 
 #endif	/* __KERNEL__ */
 
diff -urN pc-2.0.21/include/linux/ext2_fs_sb.h linux-2.0.21/include/linux/ext2_fs_sb.h
--- pc-2.0.21/include/linux/ext2_fs_sb.h	Sun Aug  4 13:38:04 1996
+++ linux-2.0.21/include/linux/ext2_fs_sb.h	Thu Sep 26 22:25:09 1996
@@ -60,6 +60,7 @@
 	int s_desc_per_block_bits;
 	int s_inode_size;
 	int s_first_ino;
+	int s_byte_swapped;
 };
 
 #endif	/* _LINUX_EXT2_FS_SB */
diff -urN pc-2.0.21/include/linux/fs.h linux-2.0.21/include/linux/fs.h
--- pc-2.0.21/include/linux/fs.h	Fri Sep 20 15:23:18 1996
+++ linux-2.0.21/include/linux/fs.h	Thu Sep 26 22:25:10 1996
@@ -659,13 +659,14 @@
 
 extern void put_super(kdev_t dev);
 unsigned long generate_cluster(kdev_t dev, int b[], int size);
+unsigned long generate_cluster_swab(kdev_t dev, int b[], int size);
 extern kdev_t ROOT_DEV;
 
 extern void show_buffers(void);
 extern void mount_root(void);
 
 #ifdef CONFIG_BLK_DEV_INITRD
-extern kdev_t real_root_dev;
+extern int real_root_dev;
 extern int change_root(kdev_t new_root_dev,const char *put_old);
 #endif
 
diff -urN pc-2.0.21/include/linux/genhd.h linux-2.0.21/include/linux/genhd.h
--- pc-2.0.21/include/linux/genhd.h	Fri Sep 20 15:23:18 1996
+++ linux-2.0.21/include/linux/genhd.h	Thu Sep 26 22:25:15 1996
@@ -11,7 +11,9 @@
 
 #include <linux/config.h>
 
+#if !defined (__mc68000__)
 #define CONFIG_MSDOS_PARTITION 1
+#endif
 
 #ifdef __alpha__
 #define CONFIG_OSF_PARTITION 1
@@ -19,6 +21,14 @@
 
 #if defined(__sparc__) || defined(CONFIG_SMD_DISKLABEL)
 #define CONFIG_SUN_PARTITION 1
+#endif
+
+#if defined(CONFIG_AMIGA) && !defined(CONFIG_AMIGA_PARTITION)
+#define CONFIG_AMIGA_PARTITION 1
+#endif
+
+#if defined(CONFIG_ATARI) && !defined(CONFIG_ATARI_PARTITION)
+#define CONFIG_ATARI_PARTITION 1
 #endif
 
 /* These two have identical behaviour; use the second one if DOS fdisk gets
diff -urN pc-2.0.21/include/linux/if_pppvar.h linux-2.0.21/include/linux/if_pppvar.h
--- pc-2.0.21/include/linux/if_pppvar.h	Tue Mar  5 09:01:27 1996
+++ linux-2.0.21/include/linux/if_pppvar.h	Sat Aug 24 14:47:56 1996
@@ -93,7 +93,7 @@
 	__s32		magic;		/* magic value for structure	*/
 
 	/* Bitmapped flag fields. */
-	__u8		inuse;		/* are we allocated?		*/
+	__u32		inuse;		/* are we allocated?		*/
 	__u8		escape;		/* 0x20 if prev char was PPP_ESC*/
 	__u8		toss;		/* toss this frame		*/
 
diff -urN pc-2.0.21/include/linux/kd.h linux-2.0.21/include/linux/kd.h
--- pc-2.0.21/include/linux/kd.h	Sun Aug  4 14:10:34 1996
+++ linux-2.0.21/include/linux/kd.h	Thu Sep 26 22:51:26 1996
@@ -132,6 +132,27 @@
 
 #define KDSIGACCEPT	0x4B4E	/* accept kbd generated signals */
 
+struct hwclk_time {
+	unsigned	sec;	/* 0..59 */
+	unsigned	min;	/* 0..59 */
+	unsigned	hour;	/* 0..23 */
+	unsigned	day;	/* 1..31 */
+	unsigned	mon;	/* 0..11 */
+	unsigned	year;	/* 70... */
+	int		wday;	/* 0..6, 0 is Sunday, -1 means unknown/don't set */
+};
+
+#define KDGHWCLK        0x4B50	/* get hardware clock */
+#define KDSHWCLK        0x4B51  /* set hardware clock */
+
+struct kbd_repeat {
+	int delay;	/* in msec; <= 0: don't change */
+	int rate;	/* in msec; <= 0: don't change */
+};
+
+#define KDKBDREP        0x4B52  /* set keyboard delay/repeat rate;
+				 * actually used values are returned */
+
 /* note: 0x4B00-0x4B4E all have had a value at some time;
    don't reuse for the time being */
 /* note: 0x4B60-0x4B6D, 0x4B70, 0x4B71 used above */
diff -urN pc-2.0.21/include/linux/keyboard.h linux-2.0.21/include/linux/keyboard.h
--- pc-2.0.21/include/linux/keyboard.h	Sun Oct  8 11:37:07 1995
+++ linux-2.0.21/include/linux/keyboard.h	Sat Aug 24 14:48:12 1996
@@ -299,7 +299,7 @@
 #define K_F243		K(KT_FN,252)
 #define K_F244		K(KT_FN,253)
 #define K_F245		K(KT_FN,254)
-#define K_F246		K(KT_FN,255)
+#define K_UNDO		K(KT_FN,255)
 
 
 #define K_HOLE		K(KT_SPEC,0)
@@ -344,8 +344,10 @@
 #define K_PCOMMA	K(KT_PAD,15)	/* key-pad comma: kludge... */
 #define K_PDOT		K(KT_PAD,16)	/* key-pad dot (period): kludge... */
 #define K_PPLUSMINUS	K(KT_PAD,17)	/* key-pad plus/minus */
+#define K_PPARENL	K(KT_PAD,18)	/* key-pad left parenthesis */
+#define K_PPARENR	K(KT_PAD,19)	/* key-pad right parenthesis */
 
-#define NR_PAD		18
+#define NR_PAD		20
 
 #define K_DGRAVE	K(KT_DEAD,0)
 #define K_DACUTE	K(KT_DEAD,1)
diff -urN pc-2.0.21/include/linux/linkage.h linux-2.0.21/include/linux/linkage.h
--- pc-2.0.21/include/linux/linkage.h	Thu Jun 29 11:22:08 1995
+++ linux-2.0.21/include/linux/linkage.h	Sat Aug 24 14:48:14 1996
@@ -26,6 +26,15 @@
 #endif
 #endif
 
+#ifdef __mc68000__
+#ifdef __ELF__
+#define __ALIGN .align 4
+#define __ALIGN_STR ".align 4"
+#else
+#define __ALIGN .align 2
+#define __ALIGN_STR ".align 2"
+#endif
+#else /* !m68k */
 #if !defined(__i486__) && !defined(__i586__)
 #ifdef __ELF__
 #define __ALIGN .align 4,0x90
@@ -43,11 +52,13 @@
 #define __ALIGN_STR ".align 4,0x90"
 #endif /* __ELF__ */
 #endif /* __i486__/__i586__ */
+#endif
+
+#define ALIGN_STR __ALIGN_STR
 
 #ifdef __ASSEMBLY__
 
 #define ALIGN __ALIGN
-#define ALIGN_STRING __ALIGN_STRING
 
 #define ENTRY(name) \
   .globl SYMBOL_NAME(name); \
diff -urN pc-2.0.21/include/linux/lp_intern.h linux-2.0.21/include/linux/lp_intern.h
--- pc-2.0.21/include/linux/lp_intern.h	Mon Mar  4 21:55:14 1996
+++ linux-2.0.21/include/linux/lp_intern.h	Thu Sep 26 22:41:45 1996
@@ -16,7 +16,7 @@
 #include <linux/types.h>
 #include <linux/lp_m68k.h>
 
-int lp_internal_init(struct lp_struct *, int, int, int);
+int lp_internal_init(void);
 
 #endif
 
diff -urN pc-2.0.21/include/linux/lp_m68k.h linux-2.0.21/include/linux/lp_m68k.h
--- pc-2.0.21/include/linux/lp_m68k.h	Thu Mar 14 21:38:20 1996
+++ linux-2.0.21/include/linux/lp_m68k.h	Thu Sep 26 22:41:45 1996
@@ -13,8 +13,13 @@
  * Interrupt support added 1993 Nigel Gamble
  */
 
-#include <linux/autoconf.h>
-#include <asm/bootinfo.h>
+#include <asm/setup.h>
+
+/*
+ *  many many printers are we going to support? currently, this is the
+ *  hardcoded limit
+ */
+#define MAX_LP 5
 
 /*
  * Per POSIX guidelines, this module reserves the LP and lp prefixes
@@ -84,6 +89,17 @@
 
 #define LP_BUFFER_SIZE 1024 /*256*/
 
+enum lp_type  {
+LP_UNKNOWN = 0,
+LP_AMIGA = 1,
+LP_ATARI = 2,
+LP_MFC = 3
+};
+
+/*
+ * warning: this structure is in kernel space and has to fit in one page,
+ * i.e. must not be larger than 4k
+ */
 struct lp_struct {
 	char *name;
 	unsigned int irq;
@@ -92,18 +108,27 @@
 	int (*lp_has_pout)(int);
 	int (*lp_is_online)(int);
 	int (*lp_my_interrupt)(int);
+	int (*lp_ioctl)(int, unsigned int, unsigned long);
+	void (*lp_open)(void);		/* for module use counter */
+	void (*lp_release)(void);	/* for module use counter */
 	int flags;		/*for BUSY... */
 	unsigned int chars;	/*busy timeout */
 	unsigned int time;	/*wait time */
 	unsigned int wait;
 	struct wait_queue *lp_wait_q; /*strobe wait */
+	void *base;			/* hardware drivers internal use*/
+	enum lp_type type;
 	char lp_buffer[LP_BUFFER_SIZE];
 	int do_print;
 	unsigned long copy_size,bytes_written;
 };
 
-extern struct lp_struct lp_table[];
+extern struct lp_struct *lp_table[MAX_LP];
+extern unsigned int lp_irq;
 
-extern int lp_init(void);
+void lp_interrupt(int, void *, struct pt_regs *);
+int lp_init(void);
+int register_parallel(struct lp_struct *, int);
+void unregister_parallel(int);
 
 #endif
diff -urN pc-2.0.21/include/linux/lp_mfc.h linux-2.0.21/include/linux/lp_mfc.h
--- pc-2.0.21/include/linux/lp_mfc.h	Thu Jan  1 01:00:00 1970
+++ linux-2.0.21/include/linux/lp_mfc.h	Thu Sep 26 22:49:21 1996
@@ -0,0 +1,14 @@
+#ifndef _LINUX_LP_MFC_H_
+#define _LINUX_LP_MFC_H_
+
+/*
+ * created 6.11.95 Joerg Dorchain
+ */
+
+#include <linux/types.h>
+#include <linux/lp_m68k.h>
+
+int lp_mfc_init(void);
+
+#endif
+
diff -urN pc-2.0.21/include/linux/msdos_fs.h linux-2.0.21/include/linux/msdos_fs.h
--- pc-2.0.21/include/linux/msdos_fs.h	Fri Sep 20 14:24:09 1996
+++ linux-2.0.21/include/linux/msdos_fs.h	Thu Sep 26 22:33:13 1996
@@ -7,6 +7,7 @@
 #include <linux/fs.h>
 #include <linux/stat.h>
 #include <linux/fd.h>
+#include <asm/byteorder.h>
 
 #define MSDOS_ROOT_INO  1 /* == MINIX_ROOT_INO */
 #define SECTOR_SIZE     512 /* sector size (bytes) */
@@ -87,11 +88,22 @@
  * BE = big-endian, c: W = word (16 bits), L = longword (32 bits)
  */
 
+#ifdef __BIG_ENDIAN
+
+#define CF_LE_W(v) ((((v) & 0xff) << 8) | (((v) >> 8) & 0xff))
+#define CF_LE_L(v) (((unsigned)(v)>>24) | (((unsigned)(v)>>8)&0xff00) | \
+		    (((unsigned)(v)<<8)&0xff0000) | ((unsigned)(v)<<24)) 
+#define CT_LE_W(v) CF_LE_W(v)
+#define CT_LE_L(v) CF_LE_L(v)
+
+#else
+
 #define CF_LE_W(v) (v)
 #define CF_LE_L(v) (v)
 #define CT_LE_W(v) (v)
 #define CT_LE_L(v) (v)
 
+#endif
 
 struct msdos_boot_sector {
 	__s8	ignored[3];	/* Boot strap short or near jump */
diff -urN pc-2.0.21/include/linux/proc_fs.h linux-2.0.21/include/linux/proc_fs.h
--- pc-2.0.21/include/linux/proc_fs.h	Fri Sep 20 15:23:29 1996
+++ linux-2.0.21/include/linux/proc_fs.h	Thu Sep 26 22:31:43 1996
@@ -43,7 +43,9 @@
 	PROC_MTAB,
 	PROC_MD,
 	PROC_RTC,
-	PROC_LOCKS
+	PROC_LOCKS,
+	PROC_HARDWARE,
+	PROC_ZORRO
 };
 
 enum pid_directory_inos {
diff -urN pc-2.0.21/include/linux/sched.h linux-2.0.21/include/linux/sched.h
--- pc-2.0.21/include/linux/sched.h	Fri Sep 20 15:23:18 1996
+++ linux-2.0.21/include/linux/sched.h	Thu Sep 26 22:25:13 1996
@@ -268,6 +268,7 @@
 
 #define PF_USEDFPU	0x00100000	/* Process used the FPU this quantum (SMP only) */
 #define PF_DTRACE	0x00200000	/* delayed trace (used on m68k) */
+#define PF_ONSIGSTK	0x00400000	/* works on signal stack (m68k only) */
 
 /*
  * Limit the stack by to some sane default: root can always
diff -urN pc-2.0.21/include/linux/serial.h linux-2.0.21/include/linux/serial.h
--- pc-2.0.21/include/linux/serial.h	Fri Sep 20 14:20:55 1996
+++ linux-2.0.21/include/linux/serial.h	Thu Sep 26 22:40:44 1996
@@ -10,6 +10,10 @@
 #ifndef _LINUX_SERIAL_H
 #define _LINUX_SERIAL_H
 
+#ifdef __mc68000__
+#include <asm/serial.h>
+#else
+
 struct serial_struct {
 	int	type;
 	int	line;
@@ -204,4 +208,5 @@
 extern int register_serial(struct serial_struct *req);
 extern void unregister_serial(int line);
 #endif /* __KERNEL__ */
+#endif /* m68k */
 #endif /* _LINUX_SERIAL_H */
diff -urN pc-2.0.21/init/main.c linux-2.0.21/init/main.c
--- pc-2.0.21/init/main.c	Wed Sep 11 10:44:54 1996
+++ linux-2.0.21/init/main.c	Thu Sep 12 21:49:18 1996
@@ -49,6 +49,10 @@
 #error sorry, your GCC is too old. It builds incorrect kernels.
 #endif
 
+#ifdef __mc68000__
+#include <asm/machdep.h>
+#endif /* __mc68000__ */
+
 extern char _stext, _etext;
 extern char *linux_banner;
 
@@ -98,6 +102,7 @@
 extern void ppa_setup(char *str, int *ints);
 extern void scsi_luns_setup(char *str, int *ints);
 extern void sound_setup(char *str, int *ints);
+extern void video_setup(char *str, int *ints);
 #ifdef CONFIG_CDU31A
 extern void cdu31a_setup(char *str, int *ints);
 #endif CONFIG_CDU31A
@@ -196,13 +201,15 @@
 extern int rd_size;		/* Size of the ramdisk(s) */
 extern int rd_image_start;	/* starting block # of image */
 #ifdef CONFIG_BLK_DEV_INITRD
-kdev_t real_root_dev;
+int real_root_dev;		/* MUST be int for sysctl! */
 #endif
 #endif
 
 int root_mountflags = MS_RDONLY;
 char *execute_command = 0;
 
+int serial_debug = 0;
+
 #ifdef CONFIG_ROOT_NFS
 char nfs_root_name[NFS_ROOT_NAME_LEN] = { "default" };
 char nfs_root_addrs[NFS_ROOT_ADDRS_LEN] = { "" };
@@ -261,6 +268,9 @@
 	{ "noinitrd", no_initrd },
 #endif
 #endif
+#if defined (CONFIG_AMIGA) || defined (CONFIG_ATARI)
+	{ "video=", video_setup },
+#endif
 	{ "swap=", swap_setup },
 	{ "buff=", buff_setup },
 	{ "panic=", panic_setup },
@@ -515,8 +525,8 @@
 	loops_per_sec *= HZ;
 /* Round the value and print it */	
 	printk("ok - %lu.%02lu BogoMIPS\n",
-		(loops_per_sec+2500)/500000,
-		((loops_per_sec+2500)/5000) % 100);
+		loops_per_sec/500000,
+		(loops_per_sec/5000) % 100);
 }
 
 static void parse_root_dev(char * line)
@@ -536,6 +546,11 @@
 		{ "sdc",     0x0820 },
 		{ "sdd",     0x0830 },
 		{ "sde",     0x0840 },
+		{ "ada",     0x1c00 },
+		{ "adb",     0x1c10 },
+		{ "adc",     0x1c20 },
+		{ "add",     0x1c30 },
+		{ "ade",     0x1c40 },
 		{ "fd",      0x0200 },
 		{ "xda",     0x0d00 },
 		{ "xdb",     0x0d40 },
@@ -630,6 +645,13 @@
 		}
 		if (!strcmp(line,"debug")) {
 			console_loglevel = 10;
+#ifdef __mc68000__
+			mach_debug_init();
+#endif /* __mc68000__ */
+			continue;
+		}
+		if (!strcmp(line,"serdebug")) {
+			serial_debug = 1;
 			continue;
 		}
 		if (!strncmp(line,"init=",5)) {
@@ -754,7 +776,7 @@
 /*
  *	Activate the first processor.
  */
- 
+
 asmlinkage void start_kernel(void)
 {
 	char * command_line;
@@ -946,14 +968,14 @@
 
 #ifdef CONFIG_BLK_DEV_INITRD
 	root_mountflags = real_root_mountflags;
-	if (mount_initrd && ROOT_DEV != real_root_dev && ROOT_DEV == MKDEV(RAMDISK_MAJOR,0)) {
+	if (mount_initrd && ROOT_DEV != (kdev_t)real_root_dev && ROOT_DEV == MKDEV(RAMDISK_MAJOR,0)) {
 		int error;
 
 		pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
 		if (pid>0)
 			while (pid != wait(&i));
-		if (real_root_dev != MKDEV(RAMDISK_MAJOR, 0)) {
-			error = change_root(real_root_dev,"/initrd");
+		if ((kdev_t)real_root_dev != MKDEV(RAMDISK_MAJOR, 0)) {
+			error = change_root((kdev_t)real_root_dev,"/initrd");
 			if (error)
 				printk(KERN_ERR "Change root to /initrd: "
 				    "error %d\n",error);
diff -urN pc-2.0.21/kernel/ksyms.c linux-2.0.21/kernel/ksyms.c
--- pc-2.0.21/kernel/ksyms.c	Thu Sep 12 06:26:25 1996
+++ linux-2.0.21/kernel/ksyms.c	Sat Sep 14 15:18:33 1996
@@ -253,8 +253,10 @@
  	X(intr_count),
 
 	/* autoirq from  drivers/net/auto_irq.c */
+#ifdef CONFIG_NET
 	X(autoirq_setup),
 	X(autoirq_report),
+#endif
 
 	/* dma handling */
 	X(request_dma),
@@ -338,9 +340,11 @@
 /* all busmice */
 	X(add_mouse_randomness),
 	X(fasync_helper),
+#ifndef __mc68000__
 /* psaux mouse */
 	X(aux_device_present),
 	X(kbd_read_mask),
+#endif
 
 #ifdef CONFIG_BLK_DEV_IDE_PCMCIA
 	X(ide_register),
diff -urN pc-2.0.21/kernel/module.c linux-2.0.21/kernel/module.c
--- pc-2.0.21/kernel/module.c	Tue May 21 11:00:30 1996
+++ linux-2.0.21/kernel/module.c	Sat Aug 24 14:50:10 1996
@@ -7,6 +7,7 @@
 #include <linux/sched.h>
 #include <linux/malloc.h>
 #include <linux/config.h>
+#include <asm/pgtable.h>
 /*
  * Originally by Anonymous (as far as I know...)
  * Linux version by Bas Laarhoven <bas@vimec.nl>
@@ -245,6 +246,9 @@
 			ref->module = mp;
 		}
 	}
+
+	flush_pages_to_ram((unsigned long)mp->addr,
+			   (codesize+sizeof(long)+PAGE_SIZE-1)/PAGE_SIZE);
 
 	GET_USE_COUNT(mp) += 1;
 	if ((*rt.init)() != 0) {
diff -urN pc-2.0.21/kernel/printk.c linux-2.0.21/kernel/printk.c
--- pc-2.0.21/kernel/printk.c	Fri Jun  7 10:54:06 1996
+++ linux-2.0.21/kernel/printk.c	Sat Aug 24 14:50:12 1996
@@ -40,6 +40,10 @@
 struct wait_queue * log_wait = NULL;
 int console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
 
+#ifdef __mc68000__
+#include <asm/machdep.h>
+void (*debug_print_proc)(const char *) = 0;
+#endif
 static void (*console_print_proc)(const char *) = 0;
 static char log_buf[LOG_BUF_LEN];
 static unsigned long log_start = 0;
@@ -141,6 +145,10 @@
 			if (len < MINIMUM_CONSOLE_LOGLEVEL)
 				len = MINIMUM_CONSOLE_LOGLEVEL;
 			console_loglevel = len;
+#ifdef __mc68000__
+			if (len == 8)
+				mach_debug_init();
+#endif
 			return 0;
 	}
 	return -EINVAL;
@@ -196,6 +204,14 @@
 			(*console_print_proc)(msg);
 			p[1] = tmp;
 		}
+#ifdef __mc68000__
+		if ((msg_level == 0 || console_loglevel >= 8) && debug_print_proc) {
+			char tmp = p[1];
+			p[1] = '\0';
+			(*debug_print_proc)(msg);
+			p[1] = tmp;
+		}
+#endif
 		if (*p == '\n')
 			msg_level = -1;
 	}
diff -urN pc-2.0.21/mm/mmap.c linux-2.0.21/mm/mmap.c
--- pc-2.0.21/mm/mmap.c	Mon Sep  9 20:04:57 1996
+++ linux-2.0.21/mm/mmap.c	Thu Sep 12 21:49:32 1996
@@ -301,7 +301,11 @@
 	if (len > TASK_SIZE)
 		return 0;
 	if (!addr)
+#ifdef __mc68000__
+		addr = SHM_RANGE_START;
+#else
 		addr = TASK_SIZE / 3;
+#endif
 	addr = PAGE_ALIGN(addr);
 
 	for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
diff -urN pc-2.0.21/mm/swapfile.c linux-2.0.21/mm/swapfile.c
--- pc-2.0.21/mm/swapfile.c	Tue Jun 11 12:00:11 1996
+++ linux-2.0.21/mm/swapfile.c	Sat Aug 24 14:50:43 1996
@@ -183,6 +183,9 @@
 	if (SWP_TYPE(pte_val(pte)) != type)
 		return 0;
 	read_swap_page(pte_val(pte), (char *) page);
+#if 0 /* Is this really needed here, hasn't it been solved elsewhere? */
+	flush_page_to_ram(page);
+#endif
 	if (pte_val(*dir) != pte_val(pte)) {
 		free_page(page);
 		return 1;
diff -urN pc-2.0.21/mm/vmalloc.c linux-2.0.21/mm/vmalloc.c
--- pc-2.0.21/mm/vmalloc.c	Sun Aug  4 12:39:07 1996
+++ linux-2.0.21/mm/vmalloc.c	Sat Aug 24 14:50:44 1996
@@ -29,6 +29,7 @@
 
 static inline void set_pgdir(unsigned long address, pgd_t entry)
 {
+#ifndef __mc68000__
 	struct task_struct * p;
 
 	for_each_task(p) {
@@ -36,6 +37,7 @@
 			continue;
 		*pgd_offset(p->mm,address) = entry;
 	}
+#endif
 }
 
 static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size)
@@ -99,7 +101,11 @@
 	pgd_t * dir;
 	unsigned long end = address + size;
 
-	dir = pgd_offset(&init_mm, address);
+#if !defined(__mc68000__)
+ 	dir = pgd_offset(&init_mm, address);
+#else
+	dir = pgd_offset_k(address);
+#endif
 	flush_cache_all();
 	while (address < end) {
 		free_area_pmd(dir, address, end - address);
@@ -156,7 +162,11 @@
 	pgd_t * dir;
 	unsigned long end = address + size;
 
-	dir = pgd_offset(&init_mm, address);
+#if !defined(__mc68000__)
+ 	dir = pgd_offset(&init_mm, address);
+#else
+	dir = pgd_offset_k(address);
+#endif
 	flush_cache_all();
 	while (address < end) {
 		pmd_t *pmd = pmd_alloc_kernel(dir, address);
@@ -218,7 +228,11 @@
 	unsigned long end = address + size;
 
 	offset -= address;
-	dir = pgd_offset(&init_mm, address);
+#if !defined(__mc68000__)
+ 	dir = pgd_offset(&init_mm, address);
+#else
+	dir = pgd_offset_k(address);
+#endif
 	flush_cache_all();
 	while (address < end) {
 		pmd_t *pmd = pmd_alloc_kernel(dir, address);
diff -urN pc-2.0.21/scripts/ksymoops.cc linux-2.0.21/scripts/ksymoops.cc
--- pc-2.0.21/scripts/ksymoops.cc	Tue Apr  9 06:07:40 1996
+++ linux-2.0.21/scripts/ksymoops.cc	Sat Aug 24 14:52:41 1996
@@ -23,6 +23,9 @@
 // command-line argument, and redirect the oops-log into stdin.  Out
 // will come the EIP and call-trace in symbolic form.
 
+// Changed by Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
+// adapted to Linux/m68k
+
 //////////////////////////////////////////////////////////////////////////////
 
 // BUGS:
@@ -37,6 +40,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <ctype.h>
+#include <a.out.h>
 
 inline int strequ(char const* x, char const* y) { return (::strcmp(x, y) == 0); }
 inline int strnequ(char const* x, char const* y, size_t n) { return (::strncmp(x, y, n) == 0); }
@@ -148,32 +152,34 @@
     /* This is a hack to avoid using gcc.  We create an object file by
        concatenating objfile_head, the twenty bytes of code, and
        objfile_tail.  */
-    unsigned char objfile_head[] = {
-	0x07, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    static struct exec objfile_head = {
+	OMAGIC, code_size + 4, 0, 0, sizeof (struct nlist) * 3, 0, 0, 0
     };
-    unsigned char objfile_tail[] = {
-	0x00, 0x90, 0x90, 0x90,
-	0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
-	0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x25, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
-	'g',  'c',  'c',  '2',  '_',  'c',  'o',  'm',  
-	'p',  'i',  'l',  'e',  'd',  '.',  '\0', '_',  
-	'E',  'I',  'P',  '\0', '\0', '\0', '\0', '\0',
-	'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
-	'\0', '\0', '\0', '\0', '\0', '\0'
+    static struct {
+	unsigned char tail[4];
+	struct nlist syms[3];
+	unsigned long strsize;
+	char strings[42];
+    } objfile_tail = {
+#ifdef i386
+	{ 0x00, 0x90, 0x90, 0x90 },
+#endif
+#ifdef mc68000
+	{ 0x00, 0x00, 0x00, 0x00 },
+#endif
+	{ { (char *) 4, N_TEXT, 0, 0, 0 },
+	  { (char *) 19, N_TEXT, 0, 0, 0 },
+	  { (char *) 37, N_TEXT | N_EXT, 0, 0, 0 } },
+	42,
+	"gcc2_compiled.\0___gnu_compiled_c\0_EIP\0"
     };
     char const* objdump_command = "objdump -d oops_decode.o";
     char const* objfile_name = &objdump_command[11];
     ofstream objfile_stream(objfile_name);
 
-    objfile_stream.write(objfile_head, sizeof(objfile_head));
+    objfile_stream.write((char *) &objfile_head, sizeof(objfile_head));
     objfile_stream.write(code, code_size);
-    objfile_stream.write(objfile_tail, sizeof(objfile_tail));
+    objfile_stream.write((char *) &objfile_tail, sizeof(objfile_tail));
     objfile_stream.close();
     
     FILE* objdump_FILE = popen(objdump_command, "r");
@@ -187,6 +193,8 @@
     while (fgets(buf, sizeof(buf), objdump_FILE)) {
 	if (!strnequ(&buf[9], "<_EIP", 5))
 	    continue;
+	if (strnequ(buf, "...", 3))
+	    break;
 	if (strstr(buf, " is out of bounds"))
 	    break;
 	lines++;
@@ -205,6 +213,7 @@
 	    bp++;
 	while (isspace(*bp))
 	    bp++;
+#ifdef i386
 	if (*bp != '0') {
 	    cout << bp_0;
 	} else if (*bp_0 == 'j' || strnequ(bp_0, "call", 4)) { // a jump or call insn
@@ -218,6 +227,23 @@
 	} else {
 	    cout << bp_0;
 	}
+#endif
+#ifdef mc68000
+	if ((bp_0[0] == 'b' && bp_0[4] == ' ' && strchr("swl", bp_0[3]))
+	    || (bp_0[0] == 'd' && bp_0[1] == 'b')) {
+	    // a branch or decr-and-branch insn
+	    if (bp_0[0] == 'd') // skip register
+		while (*bp && *bp++ != ',');
+	    long rel_addr = strtoul(bp, 0, 16);
+	    ksym = find(eip_addr + rel_addr);
+	    if (ksym) {
+		*bp++ = '\0';
+		cout << bp_0 << *ksym << endl;
+	    } else
+	      cout << bp_0;
+	} else
+	  cout << bp_0;
+#endif
     }
     if (!lines)
 	clog << "Sorry, your " << objdump_command << " can't disassemble--you must upgrade your binutils." << endl;
@@ -279,6 +305,7 @@
     {
 	long eip_addr;
 	cin >> buffer;
+#ifdef i386
 	if (strequ(buffer, "EIP:") && names.valid()) {
 	    cin >> ::hex >> eip_addr;
 	    cin >> c >> c >> c;
@@ -291,6 +318,19 @@
 	    KSym* ksym = names.find(eip_addr);
 	    if (ksym)
 		cout << ">>EIP: " << *ksym << endl;
+#endif
+#ifdef mc68000
+	if (strequ(buffer, "PC:") && names.valid()) {
+	    cin >> ::hex >> eip_addr;
+	    cin >> buffer;
+	    if (!strequ(buffer, "SR:")) {
+		clog << "Please strip the line-prefixes and rerun " << program_name << endl;
+		exit(1);
+	    }
+	    KSym* ksym = names.find(eip_addr);
+	    if (ksym)
+		cout << ">>PC: " << *ksym << endl;
+#endif
 	} else if (strequ(buffer, "Trace:") && names.valid()) {
 	    long address;
 	    while ((cin >> buffer) && 
@@ -314,6 +354,9 @@
 	    while (cp < end) {
 		int c;
 		cin >> ::hex >> c;
+#ifdef mc68000
+		*cp++ = c >> 8;
+#endif
 		*cp++ = c;
 	    }
 	    names.decode(code, eip_addr);
